React Native Header Motion

repository·main·Indexed 43 days ago

https://github.com/pawicao/react-native-header-motion

A library for creating smooth, animated collapsible headers with scroll-based motion control in React Native. It provides logic and state management for deriving progress values from scroll positions, synchronizing state across components, and bridging motion context to navigation-rendered headers in Expo Router or React Navigation. Version 1.0.1 requires react-native-reanimated, react-native-gesture-handler, and react-native-worklets.

Tokens
36.5K
Snippets
100
Records
146
Agent score
54%

What's inside React Native Header Motion

  1. Explore features in the example app

    main

    The example app contains standalone screens located in example/src/app/ that demonstrate various implementation scenarios, including:

    • Header Behaviors: Simple collapsible headers with navigation bridging, pannable headers, and overscroll effects.
    • Offset Strategies: All five offset strategies provided by the library.
    • Scrollable Integrations: Support for custom scrollables like FlashList and LegendList.
    • Advanced Interactions: Multi-page pager setups, pull to refresh, consumer scroll handlers, and usage with external refs.
  2. Core features of React Native Header Motion

    main

    The library provides several key capabilities for building complex header animations:

    • Automatic Layout Measurement: Automatically measures the total header height and the collapsible portion via <HeaderMotion.Header> and <HeaderMotion.Header.Dynamic>.
    • Progress Derivation: Generates a Reanimated shared value named progress that transitions from 0 (expanded) to 1 (collapsed) based on scroll offset.
    • Scrollable Synchronization: Orchestrates scroll positions across multiple scrollables, which is useful when a header is shared across tabs or pager pages.
    • Navigation Integration: Provides bridging mechanisms to control motion within headers rendered by React Navigation or Expo Router, which typically exist outside the standard component tree.
  3. Configure headerOffsetStrategy to manage content spacing

    main

    Because the header in react-native-header-motion is absolutely positioned as an overlay, the scrollable content starts at the top of the screen and would be hidden behind the header by default. The headerOffsetStrategy prop determines how the content is pushed down to prevent this overlap.

    Available strategies:

    • 'padding' (default): Adds paddingTop equal to the measured header height.
    • 'margin': Uses marginTop instead of padding.
    • 'top': Sets top equal to the header height and adds matching paddingBottom compensation so the end of the content remains reachable. Useful when the content container needs a specific positioning context.
    • 'translate': Applies transform: [{ translateY }] equal to the header height, with paddingBottom compensation. This avoids shifting the content container's layout box.
    • 'none': No automatic offset is applied. Use this when you want full manual control over positioning (e.g., if you have an intermediate wrapper that already accounts for the header).
    <HeaderMotion.ScrollView headerOffsetStrategy="padding">
      {/* content */}
    </HeaderMotion.ScrollView>
  4. Compare Fixed progressThreshold vs. Header.Dynamic

    main

    Choose between providing a manual threshold or using dynamic measurement based on your layout requirements:

    FeatureFixed progressThresholdHeader.Dynamic
    Threshold sourceYou provide a numberMeasured from layout
    Layout dependencyNoneYes
    FlexibilityFixed at design timeAdapts to content
    Best forKnown heights, simple layoutsDynamic/unknown content sizes, or avoiding hardcoded values
  5. How to use managedRefTarget: 'inner'

    main

    When using contentContainerMode: 'renderScrollComponent', you may need to set managedRefTarget: 'inner'.

    Use 'inner' when the wrapped component exposes a controller ref on its outside but owns the actual native scroll view internally. This is required for FlashList and LegendList. Standard React Native FlatList does not require this and should use the default 'outer' value.

    const HeaderMotionFlashList = createHeaderMotionScrollable(FlashList, {
      displayName: 'HeaderMotionFlashList',
      contentContainerMode: 'renderScrollComponent',
      managedRefTarget: 'inner',
    });
  6. When bridging is not needed

    main

    If you are not using a navigation library's built-in header (e.g., you have opted out of the navigation header and are rendering your own header component directly inside the screen), you do not need to use Bridge or NavigationBridge. The header will naturally be part of the HeaderMotion subtree and can access context directly.

    <HeaderMotion>
      <MyHeader />
      <HeaderMotion.ScrollView>{/* content */}</HeaderMotion.ScrollView>
    </HeaderMotion>
  7. Summary of HeaderMotion components and hooks

    main

    Components

    • HeaderMotion: The root provider required for all features.
    • HeaderMotion.Header: The container for your header. It measures the total header height and overlays it above the scrollable content.
    • HeaderMotion.Header.Dynamic: Marks a specific section within the header whose layout defines the progressThreshold.
    • HeaderMotion.Bridge: Captures the motion context to be passed to navigation-rendered headers.
    • HeaderMotion.NavigationBridge: Re-provides the motion context inside a navigation header.
    • HeaderMotion.ScrollView: A drop-in replacement for ScrollView that tracks scroll position.
    • HeaderMotion.FlatList: A drop-in replacement for FlatList that tracks scroll position.

    Hooks

    • useMotionProgress(): Returns { progress, progressThreshold } as Reanimated shared values.
  8. How to bridge navigation headers in v1

    main

    In v1, HeaderMotion.Header is no longer a render-prop bridge for navigation. Instead, you must use a two-step bridging process:

    1. Wrap your navigation configuration in HeaderMotion.Bridge to get the context (ctx).
    2. Wrap your header component in HeaderMotion.NavigationBridge using that context.

    Inside your header component, use useMotionProgress() to access motion data.

    <HeaderMotion.Bridge>
      {(ctx) => (
        <Stack.Screen
          options={{
            header: () => (
              <HeaderMotion.NavigationBridge value={ctx}>
                <MyHeader />
              </HeaderMotion.NavigationBridge>
            ),
          }}
        />
      )}
    </HeaderMotion.Bridge>
  9. How `HeaderMotion.Header.Dynamic` works and when to use it

    main

    HeaderMotion.Header.Dynamic is a measurement tool, not a restricted animation component. Its primary purpose is to measure its own size so the library can calculate the progressThreshold (the amount of scroll required to reach progress: 1).

    When to use it: Wrap any part of your header in Header.Dynamic if that element's size should influence the collapse threshold. If an element is supposed to visually disappear as the header collapses, wrapping it in Header.Dynamic ensures the scrollable content offset is calculated correctly.

    What you can animate: You are not limited to animating only the Dynamic component. You can animate any element in the header (including HeaderMotion.Header itself) using standard Reanimated properties like opacity, scale, translateY, or color.

  10. How overscroll extrapolation works

    main
    Enabling overscroll via progressExtrapolation does not change the actual scroll behavior of the component. The scroll handler continues to track the minimum and current scroll positions as usual. Instead, extrapolation only changes how the progress value is derived from those scroll positions, unlocking a range outside the standard 0–1 bounds for your animations.
  11. How HeaderMotion handles different scroll handler approaches

    main

    Header Motion is designed to be compatible with both standard React Native callbacks and Reanimated animated handlers. It automatically detects the type of onScroll prop provided:

    1. Standard Callbacks: Bridged via scheduleOnRN to run on the JS thread.
    2. Reanimated Handlers: Composed via useComposedEventHandler to run on the UI thread.

    In both cases, the library's internal scroll tracking for header animations remains functional and does not interfere with your custom logic.