v0.5.0 · MIT · ON NPM
REACT NATIVE · REANIMATED · NEW ARCHITECTURE

Parallax
Flow

A tiny parallax ScrollView for React Native — header parallax, pull-to-zoom, depth layers, scroll-aware overlays and a seamless bounce. All on the UI thread.

$ npm install react-native-parallax-flow react-native-reanimated
01

Demos

Every screen ships in the example app — lift them straight into a project.

02

Features

Three tiny files. No theme provider, no config, no native code.

Header parallax

The header eases away slower than the body as you scroll — set the speed with a single parallaxFactor.

Pull-to-zoom

Give the header a fixed height and it zooms in on over-scroll, top edge pinned, bottom edge glued to the body.

Depth layers

Stack ParallaxLayers at different speeds to fake depth — skies drift, foregrounds rush.

Scroll-aware overlay

An overlay slot above the scroll content that reads the scroll offset — fade-in navbars in a few lines.

Corners reveal the header

Rounded body corners show the header's image behind them (bodyOverlap), not the screen background.

Seamless iOS bounce

The bottom over-scroll region is painted in the body's color (bounceColor) — the bounce stays, the flash goes.

Animated styles

bodyStyle and headerStyle accept Reanimated animated styles — morph the body as it docks.

UI-thread everything

Built on react-native-reanimated worklets. Works on the New Architecture.

03

Quick start

react-native-reanimated (≥ 3) is a peer dependency — make sure its Babel plugin is set up (Expo SDK 50+ wires it automatically).

import { ParallaxScrollView } from 'react-native-parallax-flow';

export default function Screen() {
  return (
    <ParallaxScrollView
      headerHeight={280}
      parallaxFactor={0.5}
      headerStyle={{ backgroundColor: '#0f766e' }}
      bodyStyle={{
        backgroundColor: '#fff',
        borderTopLeftRadius: 28,
        borderTopRightRadius: 28,
      }}
      header={<Hero />}
    >
      <YourContent />
    </ParallaxScrollView>
  );
}
04

API

<ParallaxScrollView />

PropType / defaultDescription
headerReactNodeContent of the parallax header region. Can be null.
childrenReactNodeThe body that slides up over the header.
headerHeightnumber · autoFixed header height. Omit to size to content. A fixed height enables pull-to-zoom.
parallaxFactornumber · 0.5Fraction of scroll speed the header moves at, clamped to [0, 1]. 1 = keeps pace, 0 = frozen.
headerParallaxboolean · trueSet false so each ParallaxLayer owns its own motion in multi-layer scenes.
headerStyleAnimatedStyleStyle for the header container. Accepts Reanimated animated styles.
bodyStyleAnimatedStyleStyle for the body wrapper — background, radius, shadow. Accepts animated styles.
overlayReactNodeRendered absolutely above the scroll content, inside the parallax context — children can call useParallaxScroll(). Never blocks scrolling.
scrollYSharedValue<number>External shared value mirroring the scroll offset, for screen-level scroll animations.
bodyOverlapnumber · from radiusHow many px the body overlaps the header, so rounded corners reveal the header content. Defaults to the body's top corner radius.
bounceColorColorValue · from bodyStyleColor painted under the body for the bottom over-scroll region — keeps the iOS bounce seamless.
contentContainerStyleViewStyleMerged into the ScrollView's contentContainerStyle.
scrollViewPropsScrollViewPropsAny other ScrollView props. onScroll / scrollEventThrottle / contentContainerStyle are managed internally.
refAnimated.ScrollViewForwarded to the inner ScrollView — scrollTo, scrollToEnd, etc.

<ParallaxLayer />

PropType / defaultDescription
factornumber · 0.5Fraction of scroll speed, [0, 1]. Lower = slower = reads as further away.
zoomOnPullboolean · falseScale the layer up on top over-scroll — best on the nearest layers.
zoomReferencenumber · 300Height the zoom ramp is normalized against — usually the header height.
styleViewStyleExtra style on top of the absolute-fill default.
pointerEvents· 'none'Layers don't eat touches by default.

useParallaxScroll()

Returns the shared scroll offset (SharedValue<number>). Available anywhere inside the component — header, overlay, body — for building custom scroll-linked effects.

05

Recipes

Fade-in navbar

The classic "title bar appears once the hero scrolls away". Render the bar through overlay so it can read the offset:

function FadeInBar({ start, end, children }) {
  const scrollY = useParallaxScroll();
  const style = useAnimatedStyle(() => {
    const t = interpolate(scrollY.value, [start, end], [0, 1], Extrapolation.CLAMP);
    return { opacity: t, transform: [{ translateY: (1 - t) * -10 }] };
  });
  return <Animated.View style={style}>{children}</Animated.View>;
}

<ParallaxScrollView header={<Hero />} overlay={<FadeInBar start={110} end={200}>…</FadeInBar>}>
  <Body />
</ParallaxScrollView>

Docking body

Pass your own shared value through scrollY and morph the body while it scrolls — here the top radius flattens as it docks under the navbar:

const scrollY = useSharedValue(0);
const dockStyle = useAnimatedStyle(() => {
  const radius = interpolate(scrollY.value, [180, 280], [28, 0], Extrapolation.CLAMP);
  return { borderTopLeftRadius: radius, borderTopRightRadius: radius };
});

<ParallaxScrollView headerHeight={380} scrollY={scrollY}
  bodyStyle={[{ backgroundColor: '#fff' }, dockStyle]} header={<Hero />}>
  <Body />
</ParallaxScrollView>

Multi-layer scene

<ParallaxScrollView headerHeight={600} headerParallax={false}
  header={
    <>
      <ParallaxLayer factor={0.05}>{/* sky — barely moves */}</ParallaxLayer>
      <ParallaxLayer factor={0.3}>{/* mountains */}</ParallaxLayer>
      <ParallaxLayer factor={0.9} zoomOnPull zoomReference={600}>
        {/* foreground — nearest, zooms on pull */}
      </ParallaxLayer>
    </>
  }>
  <Body />
</ParallaxScrollView>

Gotchas