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-expandable-bottom-sheet

v0.1.3

Published

A performant, gesture-driven expandable bottom sheet for React Native

Readme

react-native-expandable-bottom-sheet

A performant, gesture-driven bottom sheet for React Native. Works with both the Old Architecture and the New Architecture (Fabric / Bridgeless).

  • With a preview (optional): A fixed bar at the bottom shows a teaser; it does not scroll or expand. When the user taps or drags up, a separate panel slides up over the preview and the rest of the screen (with a dimmed overlay). Dismiss by tapping the overlay, the handle, or dragging down.
  • Without a preview: Nothing is visible until you open the sheet (e.g. via a button and ref.expand()). The sheet then appears from the bottom like a modal.

Installation

yarn add react-native-expandable-bottom-sheet
# or
npm install react-native-expandable-bottom-sheet

Peer dependencies (install them if you don't already):

  • react >= 18
  • react-native >= 0.72
  • react-native-reanimated >= 3 (see version table below)
  • react-native-gesture-handler >= 2.14

After installing, rebuild your native app:

# iOS
cd ios && pod install && cd ..
npx react-native run-ios

# Android
npx react-native run-android

Important: Adding this library (and its peer deps) installs native code. A JS-only reload is not enough — you must do a full native rebuild (see Troubleshooting).

Peer Dependency Versions

Pick the right versions of Reanimated and Gesture Handler for your React Native version:

| React Native | react-native-reanimated | react-native-gesture-handler | Architecture | |---|---|---|---| | 0.72 – 0.74 | ~3.15.0 | ^2.14 | Old or New | | 0.75 – 0.77 | ~3.18.0 | ^2.14 | Old or New | | 0.78 – 0.80 | ~3.19.0 | ^2.14 | Old or New | | 0.81+ | ^4.x | ^2.21 | New Architecture only |

Important: Each Reanimated 3.x minor version only supports a narrow range of React Native versions. Check the Reanimated compatibility table for exact version pairings. Reanimated 3.x does not support React Native 0.81+. If you're on RN 0.81 or later you must use Reanimated 4.x (which requires the New Architecture). Reanimated 4.x also requires react-native-worklets — install it alongside Reanimated.

Babel config

Make sure the Reanimated Babel plugin is last in your app's babel.config.js:

module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: ['react-native-reanimated/plugin'], // must be last
};

Quick Start

import { ExpandableBottomSheet } from 'react-native-expandable-bottom-sheet';
import { GestureHandlerRootView } from 'react-native-gesture-handler';

function MyScreen() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <View style={{ flex: 1 }}>
        {/* Your main screen content */}
        <ScrollView>{/* ... */}</ScrollView>
        <ExpandableBottomSheet
          collapsedHeight={100}
          expandedHeight={0.7}
          renderPreview={() => (
            <View style={styles.previewBar}>
              <Text>Swipe up for details</Text>
            </View>
          )}
          renderHeader={() => (
            <View style={styles.handle}>
              <View style={styles.handleBar} />
              <Text>Details</Text>
            </View>
          )}
          onExpand={() => console.log('opened')}
          onCollapse={() => console.log('closed')}
        >
          <Text>Any scrollable content you want here</Text>
        </ExpandableBottomSheet>
      </View>
    </GestureHandlerRootView>
  );
}

No preview: open from a button (modal-style)

Omit renderPreview when you want to hide the sheet until the user acts (e.g. taps a button). Nothing is shown on screen; use a ref to open the sheet:

import { useRef } from 'react';
import { ExpandableBottomSheet, type ExpandableBottomSheetRef } from 'react-native-expandable-bottom-sheet';

function MyScreen() {
  const sheetRef = useRef<ExpandableBottomSheetRef>(null);

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <View style={{ flex: 1 }}>
        <Button title="Open sheet" onPress={() => sheetRef.current?.expand()} />
        <ExpandableBottomSheet ref={sheetRef} collapsedHeight={80}>
          <Text>Content when expanded</Text>
        </ExpandableBottomSheet>
      </View>
    </GestureHandlerRootView>
  );
}

The ref exposes expand() and collapse() so you can control the sheet imperatively.

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | renderPreview | () => ReactNode | — | Optional. Fixed bar at the bottom (does not scroll or expand). The entire preview area responds to tap and drag-up gestures to open the sheet, which slides over the preview. Omit for a modal-style sheet that only appears when opened via ref (e.g. button). | | children | ReactNode | required | Scrollable content when expanded. | | renderHeader | () => ReactNode | (default handle) | Header above the scroll area. Replaces the default handle when provided. | | expandedHeight | number (0–1) | 0.65 | Expanded panel height as a fraction of screen height. | | collapsedHeight | number | 120 | Height of the collapsed bar (px). | | overlayColor | string | "rgba(0,0,0,0.5)" | Backdrop color when expanded. | | overlayOpacity | number (0–1) | 0.5 | Max overlay opacity when fully expanded. | | animationDuration | number | 250 | Expand/collapse animation duration (ms). | | dragThreshold | number (0–1) | 0.15 | Fraction of expanded height to drag to commit. | | velocityThreshold | number | 200 | Min fling velocity (px/s) to commit. | | onExpand | () => void | — | Called when expansion completes. | | onCollapse | () => void | — | Called when collapse completes. | | respectReduceMotion | boolean | false | If true, skips expand/collapse animations (e.g. for accessibility). | | containerStyle | ViewStyle | — | Style for the outer wrapper. | | panelStyle | ViewStyle | — | Style for the expanded panel. | | scrollViewProps | ScrollViewProps | — | Props passed to the internal ScrollView. |

New Architecture

This library is pure JavaScript/TypeScript — it has no native modules of its own. It works with both the Old Architecture and the New Architecture (Fabric / Bridgeless) as long as the peer dependencies are set up correctly.

For React Native 0.81+ (New Architecture enabled by default):

  1. Install react-native-reanimated@^4 and react-native-worklets:
    npm install react-native-reanimated@^4 react-native-worklets react-native-gesture-handler
  2. Add the Reanimated Babel plugin (see Babel config above).
  3. Clean rebuild your native app (see below).

Local Development (linking via file:)

When you install this library locally (e.g. npm install file:../react-native-expandable-bottom-sheet), npm/yarn creates a symlink. Metro follows it and resolves react-native from the library's node_modules instead of your app's, which causes crashes.

Fix: Update your app's metro.config.js to block the library's node_modules and force all dependencies to resolve from your app:

const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');

// Path to the local library
const libraryRoot = path.resolve(__dirname, '../react-native-expandable-bottom-sheet');

const config = {
  watchFolders: [libraryRoot],
  resolver: {
    nodeModulesPaths: [path.resolve(__dirname, 'node_modules')],
    blockList: [
      new RegExp(
        path.resolve(libraryRoot, 'node_modules').replace(/[/\\]/g, '[/\\\\]') + '/.*',
      ),
    ],
  },
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

Adjust libraryRoot to the correct relative path for your setup. After this change, restart Metro with --reset-cache:

npx react-native start --reset-cache

This is only needed for local file: linking during development. When the package is installed from npm, no Metro config changes are required.

Troubleshooting

Cannot read property 'OS' of undefined or PlatformConstants could not be found

These errors mean Metro is resolving react-native from the wrong node_modules. This happens when linking locally via file:. See Local Development above for the fix.

If you're not using file: linking and still see PlatformConstants, it means the native binary wasn't rebuilt after installing native dependencies:

# 1. Clear caches
npx react-native start --reset-cache

# 2. iOS: reinstall pods and rebuild
cd ios
rm -rf Pods Podfile.lock build
pod install
cd ..
npx react-native run-ios

# 3. Android: clean and rebuild
cd android && ./gradlew clean && cd ..
npx react-native run-android

If the error persists, delete DerivedData (Xcode → Product → Clean Build Folder), or delete ios/build and rebuild.

White screen / nothing renders

Make sure GestureHandlerRootView wraps your root component with style={{ flex: 1 }}. Without it, gesture detection and layout won't work correctly.

Platform Notes

Works on iOS and Android with bare React Native (no Expo). Expo bare workflow should work if the peer dependencies are installed. Expo Go is not supported.

Example App

The example/ directory contains a React Native 0.76 app that demonstrates both modes of the component:

  • Now Playing — A music player preview bar always visible at the bottom. Tap or swipe up to reveal the full playlist queue. Shows how renderPreview can display teaser content.
  • Comments — A button-triggered comment section with no preview. Tap "View Comments" to expand. Shows the modal-style usage via ref.expand().

To run it:

cd example
npm install
cd ios && bundle install && LANG=en_US.UTF-8 bundle exec pod install && cd ..
npx react-native run-ios

See CONTRIBUTING.md for more details on the development setup.

Contributing

Issues and pull requests are welcome! See CONTRIBUTING.md for setup instructions, development workflow, and PR guidelines.