Skip to content

NativeSheet ​

A declarative, controlled sheet. It presents when visible is true and dismisses when it becomes false. By default it is a bottom sheet; pass variant for any other type.

snapPoints ['50%']
snapPoints ['90%']
'small' (25%)

Basic usage ​

tsx
import {useState} from 'react';
import {Button, Text} from 'react-native';
import {NativeSheet} from 'react-native-universal-sheet';

export function Example() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button title="Open" onPress={() => setOpen(true)} />
      <NativeSheet visible={open} onDismiss={() => setOpen(false)}>
        <Text style={{padding: 20}}>Hello from a bottom sheet</Text>
      </NativeSheet>
    </>
  );
}

NativeSheet renders nothing in place. The provider renders the sheet.

Props ​

NativeSheet accepts every sheet option plus these:

PropTypeDefaultDescription
visiblebooleanrequiredPresents when true, dismisses when false
childrenReactNoderequiredThe sheet content
idstringgeneratedStable id, if you want to call snapTo etc. on it
onDismiss() => void—After the sheet closes for any reason: swipe, backdrop, back button, dismiss() or unmount
onSnapChange(index: number) => void—The user or code moved the sheet to another snap point
onPresent() => void—After the sheet finishes appearing

Most used options:

OptionTypeDefault
variantSheetVariant'bottomSheet'
snapPointsSnapPoint[]['large'] (90%)
initialSnapPointSnapPointfirst snap point
sizing'peek' | 'half' | 'medium' | 'expanded' | 'full'—
enableDragbooleantrue for sheets
enableBackdropbooleantrue for modal behaviour
appearanceSheetAppearanceplatform defaults
stylesSheetStyles—
presentation'stack' | 'replace''stack'
behavior'modal' | 'nonModal' | 'persistent'from variant

Examples ​

Two heights, start at the smaller ​

tsx
<NativeSheet
  visible={open}
  snapPoints={['40%', '90%']}
  initialSnapPoint="40%"
  onSnapChange={index => console.log(index === 1 ? 'expanded' : 'collapsed')}
  onDismiss={() => setOpen(false)}>
  <Comments />
</NativeSheet>

Control height from code ​

Give it an id and use the manager:

tsx
const sheet = useNativeSheet();

<NativeSheet id="details" visible={open} snapPoints={['40%', '90%']} onDismiss={() => setOpen(false)}>
  <Button title="Show more" onPress={() => sheet.expand('details')} />
</NativeSheet>

Size to content ​

tsx
<NativeSheet visible={open} snapPoints={['content']} onDismiss={() => setOpen(false)}>
  <ShareTargets />
</NativeSheet>

Can't be swiped away ​

tsx
<NativeSheet
  visible={saving}
  enableDrag={false}
  hardwareBackDismiss={false}
  onDismiss={() => setSaving(false)}>
  <SavingProgress />
</NativeSheet>

Close it by setting visible to false.

Custom design ​

tsx
<NativeSheet
  visible={open}
  onDismiss={() => setOpen(false)}
  snapPoints={['50%', '90%']}
  appearance={{
    backgroundColor: '#0F172A',
    cornerRadius: 24,
    handleColor: '#64748B',
    borderWidth: 1,
    borderColor: '#1E293B',
    backdropOpacity: 0.6,
  }}
  styles={{contentStyle: {padding: 24, gap: 16}}}>
  <DarkContent />
</NativeSheet>

Updating while open ​

While the sheet is visible, changes to children and most options (snapPoints, initialSnapPoint, enableDrag, enableBackdrop, enableKeyboard, enableNestedScroll, hardwareBackDismiss, autoDismissMs, margin, anchor, animation, appearance, actions, accessibilityLabel) are applied in place without re-opening it.

variant, presentation, behavior, side, sizing and styles are read when the sheet opens. To change them, close and reopen the sheet.

Changing the callbacks (onDismiss etc.) never re-presents the sheet.

NativeSheet or useNativeSheet? ​

Use NativeSheet whenUse useNativeSheet when
The sheet's open state already lives in React stateYou open sheets from event handlers
The sheet belongs to one screenYou open the same sheet from several places
You like JSXYou need the result ({ok, reason}) of presenting

Released under the MIT License.