npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-parallax-flow

v0.5.0

Published

A tiny, dependency-light parallax ScrollView for React Native. Header parallax + pull-to-zoom + a body that slides over the header. Fully style-prop driven, powered by react-native-reanimated.

Readme

react-native-parallax-flow

A tiny, dependency-light parallax ScrollView for React Native.

npm license

📖 Docs & live demos → saleh2001k.github.io/react-native-parallax-flow

Demos

| Artist page | Social profile | Magazine cover | Night Ridge | | :---: | :---: | :---: | :---: | | Artist page — video hero, crossfading navbars | Social profile — cover photo, fade-in navbar | Magazine cover — depth-split layers | Night Ridge — nine-layer mountain scene | | Video hero, shrinking title, crossfading navbars | Cover photo, fade-in navbar, pull-to-zoom | Every cover element on its own depth layer | Nine-layer SVG mountain scene |

(GIFs are sped up 2×. Full tour + more demos on the docs site.)

  • 🪂 Header parallax — the header eases away slower than the body as you scroll.
  • 🔍 Pull-to-zoom — give the header a fixed height and it zooms in on over-scroll.
  • 🧊 Body slides over — the content body scrolls up on top of the header (great for the rounded-sheet look).
  • 🏔️ Multi-layer depth — stack ParallaxLayers at different speeds for a layered-scene effect.
  • 🎯 Scroll-aware overlay — an overlay slot above the scroll content that can read the scroll offset, for fade-in navbars and floating controls.
  • 🌀 Animated styles everywhereheaderStyle / bodyStyle accept Reanimated animated styles, and a scrollY prop exposes the offset for screen-level scroll animations (docking bodies, morphing radii…).
  • 🫧 Seamless iOS bounce — the bottom over-scroll region is painted in the body's color (bounceColor), so rubber-banding never flashes the screen background. The bounce itself stays.
  • 🖼️ Corners reveal the header — rounded body corners show the header's image/gradient behind them (bodyOverlap), not the screen background.
  • 🎨 Fully style-prop driven — zero opinionated colors. You paint the header and body.
  • 🧵 Runs on the UI thread via react-native-reanimated. Works on the New Architecture.

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


Install

npm install react-native-parallax-flow react-native-reanimated
# or
bun add react-native-parallax-flow react-native-reanimated

react-native-reanimated is a peer dependency (>= 3). Make sure its Babel plugin is set up — in Expo SDK 50+ babel-preset-expo wires it for you; in a bare app add 'react-native-worklets/plugin' (Reanimated 4) or 'react-native-reanimated/plugin' (Reanimated 3) as the last entry in babel.config.js.


Quick start

Auto-height header (sizes to its content)

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

export default function Screen() {
  return (
    <ParallaxScrollView
      headerStyle={{ backgroundColor: '#4f46e5' }}
      bodyStyle={{
        backgroundColor: '#fff',
        borderTopLeftRadius: 28,
        borderTopRightRadius: 28,
      }}
      header={
        <View style={{ padding: 24, paddingTop: 80 }}>
          <Text style={{ color: '#fff', fontSize: 30, fontWeight: '800' }}>
            Hello parallax
          </Text>
        </View>
      }
    >
      <YourContent />
    </ParallaxScrollView>
  );
}

Fixed-height header (unlocks pull-to-zoom)

<ParallaxScrollView
  headerHeight={280}
  parallaxFactor={0.5}
  headerStyle={{ backgroundColor: '#0f766e' }}
  bodyStyle={{ backgroundColor: '#fff', borderTopLeftRadius: 28, borderTopRightRadius: 28 }}
  header={<Hero />}
>
  <YourContent />
</ParallaxScrollView>

Props

| Prop | Type | Default | Notes | | ----------------------- | ------------------------------------- | ------- | ----- | | header | ReactNode | — | Content of the parallax header region. Can be null. | | children | ReactNode | — | The body that slides up over the header. | | headerHeight | number | auto | Fixed header height. Omit / undefined / 0 → header sizes to its content. A fixed height enables pull-to-zoom. | | parallaxFactor | number | 0.5 | Fraction of the body's scroll speed the header moves at, in [0, 1]. 1 = header keeps pace with the body (no parallax). 0 = header frozen in place (max parallax). 0.5 = half speed. Out-of-range values are clamped. | | headerParallax | boolean | true | Set false to stop the header container from transforming — for multi-layer scenes where each ParallaxLayer owns its own motion. | | headerStyle | StyleProp<AnimatedStyle<ViewStyle>> | — | Style for the header container — paint background / border here. Accepts Reanimated animated styles. | | bodyStyle | StyleProp<AnimatedStyle<ViewStyle>> | — | Style for the body wrapper — background, radius, shadow live here. Accepts Reanimated animated styles (see the docking recipe). | | contentContainerStyle | StyleProp<ViewStyle> | — | Merged into the ScrollView's contentContainerStyle (already gets flexGrow: 1). | | scrollViewProps | Omit<ScrollViewProps, 'onScroll' \| 'scrollEventThrottle' \| 'contentContainerStyle'> | — | Any other ScrollView props (e.g. showsVerticalScrollIndicator, refreshControl). The omitted props are managed internally. | | overlay | ReactNode | — | Rendered absolutely above the scroll content but inside the parallax context — its children can call useParallaxScroll(). Container is pointerEvents="box-none", so it never blocks scrolling. Perfect for fade-in navbars. | | scrollY | SharedValue<number> | — | Optional external shared value mirroring the scroll offset. Pass your own useSharedValue(0) to drive scroll-linked animations outside the component tree (e.g. an animated bodyStyle built at the screen level). Inside header / overlay / body children, use useParallaxScroll() instead. | | bodyOverlap | number | from bodyStyle radius | How many px the body overlaps the header's bottom edge, so rounded body corners reveal the header content (image, gradient) instead of the screen background. Defaults to the largest top corner radius in a static bodyStyle. Pass 0 to disable, or a custom value for non-radius cutouts (zig-zag edges etc.). | | bounceColor | ColorValue | from bodyStyle | Color painted under the body for the bottom over-scroll (bounce) region. Defaults to the backgroundColor found in a static bodyStyle. See the gotcha below. | | ref | Ref<Animated.ScrollView> | — | Forwarded to the inner Animated.ScrollView — call scrollTo, scrollToEnd, etc. |


How the parallax works

The header lives in the ScrollView's normal flow, so it already moves up by scrollY as you scroll. To slow it, the component translates it back down by scrollY * (1 - parallaxFactor) (clamped at 0). The net header speed is therefore scrollSpeed * parallaxFactor — lower factor = slower header = more parallax. The clamp stops over-scroll bounce from dragging the header below the top.

With a fixed headerHeight, the header additionally scales up by 1 + (-scrollY / headerHeight) when you pull past the top (scrollY < 0), giving the pull-to-zoom effect. The translate math is identical in both modes, so parallaxFactor behaves the same whether or not you set a height.

The body is rendered after the header, so it has a higher z-order and slides over it. LinearTransition keeps height changes smooth (the "flow").


Multi-layer depth (ParallaxLayer)

Stack several layers inside the header, each moving at its own speed, to fake depth — distant things drift slowly, near things rush past. Put ParallaxLayers in the header and turn off the container's own transform with headerParallax={false}.

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

<ParallaxScrollView
  headerHeight={600}
  headerParallax={false}
  header={
    <>
      <ParallaxLayer factor={0.05}>{/* sky — barely moves (far) */}</ParallaxLayer>
      <ParallaxLayer factor={0.3}>{/* mountains */}</ParallaxLayer>
      <ParallaxLayer factor={0.6}>{/* hills */}</ParallaxLayer>
      <ParallaxLayer factor={0.9} zoomOnPull zoomReference={600}>
        {/* foreground — moves most (near) + zooms on pull */}
      </ParallaxLayer>
    </>
  }
>
  <Body />
</ParallaxScrollView>;

Each layer is position: absolute + inset 0 by default, so they overlap. Fill them with anything — react-native-svg shapes, <Image>s, plain Views. See the layers showcase for a full mountain scene.

ParallaxLayer props

| Prop | Type | Default | Notes | | --------------- | --------------------- | -------- | ----- | | factor | number | 0.5 | Fraction of scroll speed, [0, 1]. Lower = slower = further away. Clamped. | | zoomOnPull | boolean | false | Scale the layer up on top over-scroll (best on the nearest layers). | | zoomReference | number | 300 | Height (px) the zoom ramp is normalized against — usually the header height. | | style | StyleProp<ViewStyle>| — | Extra style on top of the absolute-fill default. | | pointerEvents | 'auto' \| 'none' \| 'box-none' \| 'box-only' | 'none' | Layers don't eat touches by default. |

ParallaxLayer must be rendered inside a ParallaxScrollView (it reads the shared scroll offset from context). useParallaxScroll() is also exported if you want to build a custom layer.

Recipes

Fade-in navbar (appears on scroll)

The classic "title bar fades in once the hero scrolls away" pattern (profiles, product pages, album screens). Render the bar through the overlay prop so it can read the scroll offset via useParallaxScroll():

import Animated, {
  Extrapolation,
  interpolate,
  useAnimatedStyle,
} from 'react-native-reanimated';
import { ParallaxScrollView, useParallaxScroll } from 'react-native-parallax-flow';

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={[{ height: 96, backgroundColor: '#fff' }, style]}>{children}</Animated.View>;
}

<ParallaxScrollView
  headerHeight={300}
  header={<Hero />}
  overlay={
    <FadeInBar start={110} end={200}>
      <Text>Screen title</Text>
    </FadeInBar>
  }
>
  <Body />
</ParallaxScrollView>;

See the profile, product and playlist showcases in the example app for complete screens built on this.

Docking body (scroll-morphing bodyStyle)

bodyStyle accepts animated styles. Pass your own shared value through the scrollY prop and the body can morph while it scrolls — here its top radius flattens as it docks under the navbar:

import Animated, {
  Extrapolation,
  interpolate,
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';

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>;

The product showcase pairs this with a fade-in navbar.

Sheet-style body (rounded corners + shadow)

<ParallaxScrollView
  headerHeight={300}
  bodyStyle={{
    backgroundColor: '#fff',
    borderTopLeftRadius: 28,
    borderTopRightRadius: 28,
    shadowColor: '#000',
    shadowOpacity: 0.12,
    shadowRadius: 12,
    shadowOffset: { width: 0, height: -4 },
    elevation: 8,
  }}
  header={<Hero />}
>
  ...
</ParallaxScrollView>

Full-bleed image hero

import { Image } from 'expo-image';

<ParallaxScrollView
  headerHeight={340}
  parallaxFactor={0.6}
  header={
    <View style={{ flex: 1 }}>
      <Image source={URL} style={StyleSheet.absoluteFill} contentFit="cover" />
    </View>
  }
>
  ...
</ParallaxScrollView>

Programmatic scroll (forwardRef)

import { useRef } from 'react';
import type Animated from 'react-native-reanimated';

const ref = useRef<Animated.ScrollView>(null);

<ParallaxScrollView ref={ref} header={<Hero />}>...</ParallaxScrollView>;

// later:
ref.current?.scrollTo({ y: 0, animated: true });

Edge cases & gotchas

  • Rounded body corners — the body overlaps the header by its top corner radius (bodyOverlap), so the corner cutouts show the header's image or gradient at rest instead of the screen background peeking through. Leave room in the header design: its bottom bodyOverlap px sit under the body.
  • iOS bottom bounce — rubber-banding past the bottom reveals whatever is behind the ScrollView (usually your screen background). If your screen bg is red and your body is white, the bounce flashes red under the white body. The component paints a tall "tail" under the body in bounceColor (auto-derived from a static bodyStyle's backgroundColor) so the bounce stays seamless — the bounce itself is untouched. Pass bounceColor explicitly when the body surface color can't be derived (animated or transparent bodies).
  • parallaxFactor is clamped to [0, 1] — passing 2 behaves like 1.
  • headerHeight={0} is treated the same as omitting it (auto mode); a 0-px header is never useful.
  • Short body (less than a screen) still fills the viewport — the content container uses flexGrow: 1, so no gap appears under the body.
  • onScroll / scrollEventThrottle are managed internally and can't be overridden via scrollViewProps (they're typed out).
  • Reanimated peer must be installed and its Babel plugin enabled, or the worklets won't run.

Example app

A full Expo (SDK 54) + expo-router demo lives in example/ with showcases for every mode (auto header, fixed header + zoom, image hero, an interactive playground, programmatic scroll, and edge cases) plus three real-app screens you can lift straight into a project:

  • Artist page — muted video hero (expo-video), a big title that shrinks away, and two navbars crossfading (floating controls out, solid title bar in).
  • Social profile — cover photo, fade-in navbar, overlapping avatar, stats, photo grid.
  • Product detail — image hero, fade-in title/price bar, color + size pickers, sticky add-to-cart, docking body.
  • Album playlist — Spotify-style artwork that shrinks/fades, gradient scene, fade-in title bar, track list.
  • Travel destination — frosted-glass info card on the hero, blur fade-in navbar, blur back button, gallery.
  • Event tickets — poster hero, lineup, frosted ticket bar the content scrolls under.
  • Restaurant menu — food hero, blur fade-in bar, sectioned menu with dish photos.
  • Solar system — Saturn built from stacked SVG layers in deep space; the planet zooms on pull.
  • Magazine cover — masthead, watermark, color block, photo and headline each on their own depth layer.
  • Boarding pass — perforated zig-zag body edge; the sky hero shows through the teeth (bodyOverlap).

The showcases also demo four back-button styles (translucent chip, frosted blur circle, solid circle, bare glyph) — see BackButton in example/components/Showcase.tsx.

cd example
bun install      # or npm install
bun ios          # or bun android / bun start

License

MIT © Saleh Ayman