Migration from v0
This guide covers migration from v0.3.x to the current v1 API. The API change is substantial, but the migration is usually straightforward and the result is a much better developer experience.
If you are not migrating yet and need the old docs, see the README on the v0 branch.
What changed at a high level
The biggest shift is from a prop-passing header API to a context-first header API:
HeaderMotion.Headeris no longer a render-prop bridge — it's the actual header containerAnimatedHeaderBase/HeaderBasehave been removed- Measurement wiring now lives in
HeaderMotion.HeaderandHeaderMotion.Header.Dynamic - Navigation headers use explicit
Bridge+NavigationBridge useMotionProgress()is intentionally narrower
Upgrade checklist
- Add
react-native-gesture-handleras a peer dependency if you don't already have it - Replace render-prop
HeaderMotion.HeaderwithHeaderMotion.Bridge+HeaderMotion.NavigationBridge - Replace
AnimatedHeaderBase/HeaderBasewithHeaderMotion.Header - Replace manual
measureDynamicwiring withHeaderMotion.Header.Dynamic - Remove
WithCollapsibleHeaderProps/WithCollapsiblePagedHeaderPropsusage - Update
useMotionProgress()to readprogressThresholdas aSharedValue - Review custom scrollable integrations
1. Peer dependencies changed
v1 requires:
| Package | Version |
|---|---|
react-native-gesture-handler | ^2.0.0 |
react-native-reanimated | ^4.0.0 |
react-native-worklets | >= 0.4.0 |
react-native-gesture-handler is new to the peer surface because header panning is built on it.
2. HeaderMotion.Header is no longer a render prop
Before (v0.3.x)
<HeaderMotion.Header>
{(headerProps) => (
<Stack.Screen
options={{
header: () => <MyHeader {...headerProps} />,
}}
/>
)}
</HeaderMotion.Header>
After (v1)
<HeaderMotion.Bridge>
{(ctx) => (
<Stack.Screen
options={{
header: () => (
<HeaderMotion.NavigationBridge value={ctx}>
<MyHeader />
</HeaderMotion.NavigationBridge>
),
}}
/>
)}
</HeaderMotion.Bridge>
Inside MyHeader, call useMotionProgress() normally.
3. AnimatedHeaderBase and HeaderBase were removed
Before (v0.3.x)
function MyHeader({
progress,
progressThreshold,
measureTotalHeight,
measureDynamic,
}: WithCollapsibleHeaderProps) {
return (
<AnimatedHeaderBase onLayout={measureTotalHeight}>
<Animated.View onLayout={measureDynamic}>
{/* collapsible part */}
</Animated.View>
</AnimatedHeaderBase>
);
}
After (v1)
function MyHeader() {
const { progress, progressThreshold } = useMotionProgress();
return (
<HeaderMotion.Header>
<HeaderMotion.Header.Dynamic>
{/* collapsible part */}
</HeaderMotion.Header.Dynamic>
</HeaderMotion.Header>
);
}
Measurement wiring is handled automatically by HeaderMotion.Header and HeaderMotion.Header.Dynamic.
4. useMotionProgress() is narrower
Before: returned progress, progressThreshold, measureTotalHeight, measureDynamic.
After: returns only progress and progressThreshold.
If you need the full bridge value, use useHeaderMotionBridge().
5. progressThreshold is now a SharedValue
The prop configuration is the same (number or function), but at runtime progressThreshold from useMotionProgress() is a SharedValue<number>.
Before (v0.3.x)
const translateY = interpolate(
progress.value,
[0, 1],
[0, -progressThreshold],
Extrapolation.CLAMP
);
After (v1)
const threshold = progressThreshold.get();
const translateY = interpolate(
progress.get(),
[0, 1],
[0, -threshold],
Extrapolation.CLAMP
);
6. Removed type exports
WithCollapsibleHeaderProps and WithCollapsiblePagedHeaderProps have been removed. Headers now read from context via useMotionProgress().
7. Custom scrollable integrations
- Prefer
createHeaderMotionScrollable()for reusable custom scrollables useScrollManager()now returnsscrollablePropsandheaderMotionContextheaderMotionContext.contentContainerMinHeightreplaces oldminHeightContentContainerStyleensureScrollableContentMinHeightis the explicit opt-in for min-height behavior
Side-by-side comparison
Full before/after example
Before (v0.3.x)
import HeaderMotion, {
AnimatedHeaderBase,
type WithCollapsibleHeaderProps,
} from 'react-native-header-motion';
function Screen() {
return (
<HeaderMotion>
<HeaderMotion.Header>
{(headerProps) => (
<Stack.Screen
options={{
header: () => <MyHeader {...headerProps} />,
}}
/>
)}
</HeaderMotion.Header>
<HeaderMotion.ScrollView>{/* content */}</HeaderMotion.ScrollView>
</HeaderMotion>
);
}
function MyHeader({
progress,
progressThreshold,
measureTotalHeight,
measureDynamic,
}: WithCollapsibleHeaderProps) {
return (
<AnimatedHeaderBase onLayout={measureTotalHeight}>
<Animated.View onLayout={measureDynamic}>
{/* dynamic content */}
</Animated.View>
<Text>Sticky title</Text>
</AnimatedHeaderBase>
);
}
After (v1)
import HeaderMotion, { useMotionProgress } from 'react-native-header-motion';
function Screen() {
return (
<HeaderMotion>
<HeaderMotion.Bridge>
{(ctx) => (
<Stack.Screen
options={{
header: () => (
<HeaderMotion.NavigationBridge value={ctx}>
<MyHeader />
</HeaderMotion.NavigationBridge>
),
}}
/>
)}
</HeaderMotion.Bridge>
<HeaderMotion.ScrollView>{/* content */}</HeaderMotion.ScrollView>
</HeaderMotion>
);
}
function MyHeader() {
const { progress, progressThreshold } = useMotionProgress();
return (
<HeaderMotion.Header>
<HeaderMotion.Header.Dynamic>
{/* dynamic content */}
</HeaderMotion.Header.Dynamic>
<Text>Sticky title</Text>
</HeaderMotion.Header>
);
}