About this animation
A responsive Skia headline whose words resolve from a soft blur before a narrow accent-to-white light travels inside the letterforms.
Why this one
Most headline shimmers slide an opaque rectangle over ordinary text. This version keeps every highlight inside the glyphs with a real Skia shader, lets a tap choose the sweep's origin, and resolves into a quiet keyword glow instead of looping visual noise.
How it works
- The component measures its own container and sizes an editorial headline from that local width and height.
- Skia measures every word with
getTextWidth, then centers each supplied line without screen-level dimensions. - One continuous Reanimated progress value drives the 70 ms word stagger, the 6 px blur release, the moving gradient, and the final keyword glow.
- Each word uses a Skia
LinearGradientas its text shader, so the accent-to-white sweep is clipped by the glyphs. - Tapping the headline resets that same progress and places the sweep at the touched horizontal position.
Install
npx expo install @shopify/react-native-skia react-native-reanimated react-native-workletsOn web, follow Expo's Skia setup so CanvasKit is loaded before this component renders.
Usage
import { useRef } from 'react';
import { GradientSweepHeadline, type GradientSweepHeadlineRef } from './gradient-sweep-headline';
export function NotesHero() {
const headlineRef = useRef<GradientSweepHeadlineRef>(null);
return (
<GradientSweepHeadline
ref={headlineRef}
lines={['Turn scattered thoughts', 'into luminous', 'possibility.']}
highlightWord="possibility."
titleFont={require('./InstrumentSerif-Regular.ttf')}
/>
);
}Customize its personality or replay from a chosen horizontal origin:
<GradientSweepHeadline
ref={headlineRef}
accentColor="#9A87FF"
duration={2000}
glowIntensity={0.34}
textColor="#DDD9E8"
wordStagger={70}
/>
headlineRef.current?.replayFrom(0.5);Props
| Prop | Type | Default | Description |
|---|---|---|---|
lines | string[] | Three-line sample headline | One centered string per headline line. |
highlightWord | string | 'possibility.' | Word that retains the final accent glow. |
titleFont | DataSourceParam | System serif where available | Skia font source for the headline; pass a source on web. |
textColor | string | '#D8D5E3' | Resting headline color. |
accentColor | string | '#8C7CFF' | Sweep and keyword-glow color. |
duration | number | 1800 | Full entrance and sweep duration in milliseconds. |
wordStagger | number | 70 | Delay between word entrances in milliseconds. |
glowIntensity | number | 0.28 | Final keyword glow opacity from 0 to 1. |
onSweepComplete | () => void | — | Called after the sweep settles. |
accessibilityLabel | string | Descriptive replay instruction | Screen-reader label for the headline button. |
style | StyleProp<ViewStyle> | — | Outer-container style. |
testID | string | — | Test identifier for the outer container. |
The forwarded ref exposes replayFrom(position?), where position is a normalized horizontal coordinate from 0 to 1.
Compatibility
- Expo SDK 57
- React Native 0.86
- Reanimated 4.5 and Worklets 0.10
- React Native Skia 2.6
- iOS: supported, not yet verified on a device
- Android: supported, not yet verified on a device
- Web: supported, not yet visually verified
- Works in Expo Go because its native dependencies are included in the SDK
Accessibility
The entire headline is a labeled button with a touch target larger than 44 pt. Tapping is the direct alternative to any gesture. With reduced motion enabled, every word is immediately readable, the sweep stops, and only the soft static keyword glow remains.




