{"id":13990826,"url":"https://github.com/rgommezz/react-native-scroll-bottom-sheet","last_synced_at":"2025-04-14T08:55:33.426Z","repository":{"id":38888029,"uuid":"260984389","full_name":"rgommezz/react-native-scroll-bottom-sheet","owner":"rgommezz","description":"Cross platform scrollable bottom sheet with virtualisation support, native animations at 60 FPS and fully implemented in JS land :fire:","archived":false,"fork":false,"pushed_at":"2023-03-04T19:28:28.000Z","size":86807,"stargazers_count":1549,"open_issues_count":33,"forks_count":66,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-07T01:11:10.426Z","etag":null,"topics":["android","bottom-sheet","bottomsheet","cross-platform","ios","react-native","react-native-gesture-handler","react-native-reanimated"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rgommezz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-05-03T17:31:33.000Z","updated_at":"2025-03-25T20:04:45.000Z","dependencies_parsed_at":"2024-03-24T09:46:19.914Z","dependency_job_id":null,"html_url":"https://github.com/rgommezz/react-native-scroll-bottom-sheet","commit_stats":{"total_commits":133,"total_committers":4,"mean_commits":33.25,"dds":"0.19548872180451127","last_synced_commit":"eef584cb08894a0d876101d686926a6eff41ac6f"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rgommezz%2Freact-native-scroll-bottom-sheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rgommezz%2Freact-native-scroll-bottom-sheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rgommezz%2Freact-native-scroll-bottom-sheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rgommezz%2Freact-native-scroll-bottom-sheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rgommezz","download_url":"https://codeload.github.com/rgommezz/react-native-scroll-bottom-sheet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248852110,"owners_count":21171839,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","bottom-sheet","bottomsheet","cross-platform","ios","react-native","react-native-gesture-handler","react-native-reanimated"],"created_at":"2024-08-09T13:03:20.297Z","updated_at":"2025-04-14T08:55:33.401Z","avatar_url":"https://github.com/rgommezz.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# Scroll Bottom Sheet\n[![npm](https://img.shields.io/npm/v/react-native-scroll-bottom-sheet?color=brightgreen)](https://www.npmjs.com/package/react-native-scroll-bottom-sheet)\n[![npm bundle size](https://img.shields.io/bundlephobia/min/react-native-scroll-bottom-sheet)](https://bundlephobia.com/result?p=react-native-scroll-bottom-sheet)\n![platforms: ios, android, web](https://img.shields.io/badge/platform-ios%2C%20android-blue)\n[![license MIT](https://img.shields.io/badge/license-MIT-brightgreen)](https://github.com/rgommezz/react-native-scroll-bottom-sheet/blob/master/LICENSE)\n\nCross platform scrollable bottom sheet with virtualisation support and fully native animations, that integrates with all core scrollable components from React Native: [FlatList](https://reactnative.dev/docs/flatlist), [ScrollView](https://reactnative.dev/docs/scrollview) and [SectionList](https://reactnative.dev/docs/sectionlist). Also, it's 100% compatible with Expo.\n\n\u003cbr /\u003e\n\n![](gifs/bank.gif)  |  ![](gifs/maps.gif) |\n:---------------------:|:------------------:|\n\n\u003cbr /\u003e\n\n## Features\n- **:electron: Virtualisation support**: `FlatList` and `SectionList` components are 1st class citizens, as well as `ScrollView`\n- **:fire: Performant**: runs at 60 FPS even on low grade Android devices\n- **:white_check_mark: Horizontal mode**: allows for nice implementation of Google or Apple Maps bottom sheets types, where you have several horizontal lists embedded\n- **:gear: Minimalistic**: exposes a set of fundamental props to easily control its behaviour, supporting both Timing and Spring animations\n- **:point_down: Support for interruptions**: animations can be interrupted anytime smoothly without sudden jumps\n- **:sunglasses: Imperative snapping**: for cases where you need to close the bottom sheet by pressing an external touchable\n- **:rocket: Animate all the things**: you can animate other elements on the screen based on the bottom sheet position\n- **:muscle: No native dependencies**: fully implemented in JS land, thanks to the powerful [Gesture Handler](https://github.com/software-mansion/react-native-gesture-handler) and [Reanimated](https://github.com/software-mansion/react-native-reanimated) libraries\n- **:iphone: Expo compatible**: no need to eject to enjoy this component!\n- **:hammer_and_wrench: TS definitions**: For those of you like me who can't look back to start a project in plain JS\n\n## Installation\n\n#### npm\n\n```sh\nnpm i react-native-scroll-bottom-sheet\n```\n\n#### yarn\n```sh\nyarn add react-native-scroll-bottom-sheet\n```\n\nIf you don't use Expo, you also need to install [react-native-gesture-handler](https://github.com/software-mansion/react-native-gesture-handler) and [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) libraries along with this one.\n\nIt's recommended you install a version of gesture handler equal or higher than `1.6.0`, and for reanimated, equal or higher than `1.7.0`. Otherwise you may run into unexpected errors. This library is also compatible with reanimated 2.x, starting with `react-native-reanimated: 2.0.0-alpha.4`.\n\n## Usage\n\nThe below is an example using the core `FlatList` from React Native as the scrollable component.\n\n```tsx\nimport React from 'react';\nimport { Dimensions, StyleSheet, Text, View } from 'react-native';\nimport ScrollBottomSheet from 'react-native-scroll-bottom-sheet';\n\nconst windowHeight = Dimensions.get('window').height;\n\nfunction Example() {\n  return (\n    \u003cView style={styles.container}\u003e\n      \u003cScrollBottomSheet\u003cstring\u003e // If you are using TS, that'll infer the renderItem `item` type\n        componentType=\"FlatList\"\n        snapPoints={[128, '50%', windowHeight - 200]}\n        initialSnapIndex={2}\n        renderHandle={() =\u003e (\n          \u003cView style={styles.header}\u003e\n            \u003cView style={styles.panelHandle} /\u003e\n          \u003c/View\u003e\n        )}\n        data={Array.from({ length: 200 }).map((_, i) =\u003e String(i))}\n        keyExtractor={i =\u003e i}\n        renderItem={({ item }) =\u003e (\n          \u003cView style={styles.item}\u003e\n            \u003cText\u003e{`Item ${item}`}\u003c/Text\u003e\n          \u003c/View\u003e\n        )}\n        contentContainerStyle={styles.contentContainerStyle}\n      /\u003e\n    \u003c/View\u003e\n  );\n}\n\nconst styles = StyleSheet.create({\n  container: {\n    flex: 1,\n  },\n  contentContainerStyle: {\n    padding: 16,\n    backgroundColor: '#F3F4F9',\n  },\n  header: {\n    alignItems: 'center',\n    backgroundColor: 'white',\n    paddingVertical: 20,\n    borderTopLeftRadius: 20,\n    borderTopRightRadius: 20\n  },\n  panelHandle: {\n    width: 40,\n    height: 2,\n    backgroundColor: 'rgba(0,0,0,0.3)',\n    borderRadius: 4\n  },\n  item: {\n    padding: 20,\n    justifyContent: 'center',\n    backgroundColor: 'white',\n    alignItems: 'center',\n    marginVertical: 10,\n  },\n});\n```\n\n## Props\nThere are 2 types of props this component receives: explicit and inherited.\n\n### Explicit\nThis is the list of exclusive props that are meant to be used to customise the bottom sheet behaviour.\n\n\n| Name                      | Required | Type | Description |\n| ------------------------- | -------- | ------- | ------------|\n| `componentType`             | yes      | `string `       | 'FlatList', 'ScrollView', or 'SectionList' |\n| `snapPoints`                | yes      | `Array\u003cstring \\| number\u003e`       | Array of numbers and/or percentages that indicate the different resting positions of the bottom sheet (in dp or %), **starting from the top**. If a percentage is used, that would translate to the relative amount of the total window height. If you want that percentage to be calculated based on the parent available space instead, for example to account for safe areas or navigation bars, use it in combination with `topInset` prop |\n| `initialSnapIndex`          | yes      | `number`       | Index that references the initial resting position of the drawer, **starting from the top** |\n| `renderHandle`              | yes      |  `() =\u003e React.ReactNode`      | Render prop for the handle, should return a React Element |\n| `onSettle`                  | no       |  `(index: number) =\u003e void`       | Callback that is executed right after the bottom sheet settles in one of the snapping points. The new index is provided on the callback |\n| `animationType`             | no       | `string`        | `timing` (default)  or `spring` |\n| `animationConfig`           | no       | `TimingConfig` or `SpringConfig`         | Timing or Spring configuration for the animation. If `animationType` is `timing`, it uses by default a timing configuration with a duration of 250ms and easing fn `Easing.inOut(Easing.linear)`. If `animationType` is `spring`, it uses [this default spring configuration](https://github.com/rgommezz/react-native-scroll-bottom-sheet/blob/master/src/index.tsx#L77). You can partially override any parameter from the animation config as per your needs   |\n| `animatedPosition`          | no       |  `Animated.Value\u003cnumber\u003e`       | Animated value that tracks the position of the drawer, being: 0 =\u003e closed, 1 =\u003e fully opened |\n| `topInset`                  | no       | `number`  | This value is useful to provide an offset (in dp) when applying percentages for snapping points |\n| `innerRef`                  | no       | `RefObject`  | Ref to the inner scrollable component (ScrollView, FlatList or SectionList), so that you can call its imperative methods. For instance, calling `scrollTo` on a ScrollView. In order to so, you have to use `getNode` as well, since it's wrapped into an _animated_ component: `ref.current.getNode().scrollTo({y: 0, animated: true})` |\n| `containerStyle`            | no       | `StyleProp\u003cViewStyle\u003e`  | Style to be applied to the container (Handle and Content) |\n| `friction`                  | no       | `number`      | Factor of resistance when the gesture is released. A value of 0 offers maximum * acceleration, whereas 1 acts as the opposite. Defaults to 0.95 |\n| `enableOverScroll`          | no       | `boolean`     | Allow drawer to be dragged beyond lowest snap point |\n\n\n### Inherited\nDepending on the value of `componentType` chosen, the bottom sheet component will inherit its underlying props, being one of\n[FlatListProps](https://reactnative.dev/docs/flatlist#props), [ScrollViewProps](https://reactnative.dev/docs/scrollview#props) or [SectionListProps](https://reactnative.dev/docs/sectionlist#props), so that you can tailor the scrollable content behaviour as per your needs.\n\n## Methods\n\n#### `snapTo(index)`\n\nImperative method to snap to a specific index, i.e.\n\n```js\nbottomSheetRef.current.snapTo(0)\n```\n\n`bottomSheetRef` refers to the [`ref`](https://reactjs.org/docs/react-api.html#reactcreateref) passed to the `ScrollBottomSheet` component.\n\n## Compatibility table\nYou may add some touchable components inside the bottom sheet or several `FlatList` widgets for horizontal mode. Unfortunately, not all _interactable_ React Native components are compatible with this library. This is due to some limitations on `react-native-gesture-handler`, which this library uses behind the scenes. For that, please follow this compatibility table:\n\n| Import                       | Touchable | Flatlist    |\n| -------------------------    | --------  | -------     |\n| react-native                 | iOS       |      🚫     |\n| react-native-gesture-handler | Android   | Android, iOS|\n\n### Touchables\nAs you can see on the table, for any touchable component (`TouchableOpacity`, `TouchableHighlight`, ...) you need to have different imports depending on the platform. The below is a snippet you may find useful to abstract that into a component.\n\n```js\nimport React from \"react\";\nimport { Platform, TouchableOpacity } from \"react-native\";\nimport { TouchableOpacity as RNGHTouchableOpacity } from \"react-native-gesture-handler\";\n\nconst BottomSheetTouchable = (props) =\u003e {\n  if (Platform.OS === \"android\") {\n    return (\n      \u003cRNGHTouchableOpacity {...props} /\u003e\n    );\n  }\n\n  return \u003cTouchableOpacity {...props} /\u003e\n};\n\nexport default BottomSheetTouchable;\n```\n\n### Horizontal Mode\nFor this mode to work properly, **you have to import `FlatList` from react-native-gesture-handler** instead of react-native.\n\n```js\nimport { FlatList } from 'react-native-gesture-handler';\n\n...\n```\n\n## Limitations\nAt the moment, the component does not support updating snap points via state, something you may want to achieve when changing device orientation for instance. A temporary workaround is to leverage React keys to force a re-mount of the component. This is some illustrative code to give you an idea how you could handle an orientation change with keys:\n\n```js\nimport { useDimensions } from '@react-native-community/hooks'\n\nconst useOrientation = () =\u003e {\n  const { width, height } = useDimensions().window;\n\n  if (height \u003e width) {\n    return 'portrait'\n  }\n\n  return 'landscape'\n}\n\nconst OrientationAwareBS = () =\u003e {\n\tconst orientation = useOrientation();\n\tconst snapPoints = {\n\t\tportrait: [...],\n\t\tlandscape: [...]\n\t}\n\n\treturn (\n      \u003cScrollBottomSheet\n \t\tkey={orientation}\n        componentType=\"FlatList\"\n        snapPoints={snapPoints[orientation]}\n        initialSnapIndex={2}\n        ...\n      /\u003e\n\t);\n}\n```\n\n## Example\nThere is an Expo example application that you can play with to get a good grasp on the different customisation options. In case of Android, you can directly open the project [here](https://expo.io/@rgommezz/react-native-scroll-bottom-sheet-example). For iOS, head to the [example folder](https://github.com/rgommezz/react-native-scroll-bottom-sheet/tree/master/example) and run the project locally:\n\n```bash\n$ npm install\n\n$ npm start\n```\n\n## Typescript\nThe library has been written in Typescript, so you'll get type checking and autocompletion if you use it as well.\n\n## License\n\nMIT © [Raul Gomez Acuna](https://raulgomez.io/)\n\nIf you found this project interesting, please consider following me on [twitter](https://twitter.com/rgommezz)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frgommezz%2Freact-native-scroll-bottom-sheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frgommezz%2Freact-native-scroll-bottom-sheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frgommezz%2Freact-native-scroll-bottom-sheet/lists"}