About this animation
A layered poster hero with touch parallax and a scroll fade, with configurable background image, subject image, headline, and fonts.
How it works
- Three layers (background, title and subject) share one tilt value. Each layer moves by a different factor of that value, which is what produces the depth.
- The tilt follows the pointer on web and a pan gesture on touch screens, and springs back to center when the gesture ends.
- Scrolling drives a second shared value: the hero scales slightly, drifts and fades into
baseColorwhilechildrenscroll up over it. - Floating sparks in the accent color drift between the layers. They are generated once per
sparkCount.
Title lines use a line height of 1.2 times their font size plus vertical padding to protect custom font ascenders and descenders on native platforms. The title size also accounts for the container height and line count, so the full stack fits in landscape and short embedded containers. It renders after the container has been measured.
Install
npx expo install expo-image expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-workletsWrap your app in GestureHandlerRootView if it is not already.
Usage
import PosterParallaxHero from './poster-parallax-hero';
export function HeroScreen() {
return (
<PosterParallaxHero
background={require('./assets/scenery.jpg')}
subject={require('./assets/subject.png')}
eyebrow="Now streaming"
titleLines={['NORTH', 'RIDGE']}
>
{/* Content that scrolls over the hero */}
</PosterParallaxHero>
);
}subject needs a transparent background so the title shows behind it.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
background | ImageSource | number | Required | Full-bleed scenery behind everything (sky, landscape). |
subject | ImageSource | number | Required | Transparent cutout of the subject; it sits in front of the title. |
titleLines | string[] | Required | Title words, one per line, drawn between the scenery and the subject. |
eyebrow | string | undefined | Small label above the title. |
accentColor | string | '#FFB547' | Color of the floating sparks and the eyebrow rule. |
baseColor | string | '#06090B' | Color the hero fades into as it scrolls away; also the page background. |
titleColor | string | '#F4F1EA' | Color of the hero title. |
titleFontFamily | string | undefined | Title font. Bold condensed display faces work best. |
fontFamily | string | undefined | Font family for interface text. |
tiltStrength | number | 1 | Strength of the pointer / drag parallax (0 disables it). |
sparkCount | number | 22 | Number of floating sparks (0 disables them). |
children | ReactNode | undefined | Content that scrolls up over the faded hero. |
style | StyleProp<ViewStyle> | undefined | Style applied to the outer container. |
onInteraction | () => void | undefined | Called when a hover or drag interaction begins. |
testID | string | undefined | Root ID; ${testID}-scroll identifies the scroll and parallax surface. Suffix IDs are omitted when testID is absent. |
Compatibility
- Expo SDK 58
- React Native 0.88
- Reanimated 4.7
- Gesture Handler 3.3
- iOS, Android and web: supported
- Works in Expo Go
Accessibility
- The title is a single header whose label is the full title, so screen readers read it as one phrase rather than one word per line.
- With Reduce Motion on, there are no sparks and the parallax tilt is disabled.
Customization notes
Parallax follows pointer, gesture, and scroll position. tiltStrength controls its intensity.




