This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
This is documentation for the next SDK version. For up-to-date documentation, see the latest version (SDK 57).
BottomSheet
A modal sheet that slides up from the bottom of the screen.
A modal sheet that slides up from the bottom of the screen. The sheet's visibility is controlled — toggle isPresented from React state and dismiss it from onDismiss (called when the user swipes down or taps the overlay).
On iOS, to show a bottom sheet on top of another, nest the secondBottomSheetinside the first sheet's content rather than beside it. This is a limitation of the underlying SwiftUIsheetmodifier. See how to present multiple sheets for more information.


Installation
- npx expo install @expo/ui- yarn expo install @expo/ui- pnpm expo install @expo/ui- bun expo install @expo/uiIf you are installing this in an existing React Native app, make sure to install expo in your project.
Usage
Basic bottom sheet
import { useState } from 'react'; import { useColorScheme } from 'react-native'; import { Host, Column, Button, BottomSheet, Text } from '@expo/ui'; export default function BottomSheetExample() { const [isPresented, setIsPresented] = useState(false); const colorScheme = useColorScheme(); const ink = { color: colorScheme === 'dark' ? '#FFFFFF' : '#000000' }; return ( <> <Host matchContents> <Button label="Open sheet" onPress={() => setIsPresented(true)} /> </Host> <BottomSheet isPresented={isPresented} onDismiss={() => setIsPresented(false)}> <Column spacing={12}> <Text textStyle={{ ...ink, fontSize: 18, fontWeight: '700' }}>Sheet contents</Text> <Text textStyle={ink}>Drag down or tap the overlay to dismiss.</Text> <Button label="Close" onPress={() => setIsPresented(false)} /> </Column> </BottomSheet> </> ); }
Hiding the drag indicator
Pass showDragIndicator={false} for sheets without a handle.
import { useState } from 'react'; import { useColorScheme } from 'react-native'; import { Host, Button, BottomSheet, Text } from '@expo/ui'; export default function BottomSheetNoIndicatorExample() { const [isPresented, setIsPresented] = useState(false); const colorScheme = useColorScheme(); const ink = { color: colorScheme === 'dark' ? '#FFFFFF' : '#000000' }; return ( <> <Host matchContents> <Button label="Open" onPress={() => setIsPresented(true)} /> </Host> <BottomSheet isPresented={isPresented} onDismiss={() => setIsPresented(false)} showDragIndicator={false}> <Text textStyle={ink}>No drag handle.</Text> </BottomSheet> </> ); }
Content padding
The sheet insets its content by default. Pass contentPadding to change that inset — 0 lets a row, image, or divider reach the sheet's edges.
import { useState } from 'react'; import { Host, BottomSheet, Button, Column, Text } from '@expo/ui'; export default function BottomSheetContentPaddingExample() { const [isPresented, setIsPresented] = useState(false); return ( <> <Host matchContents> <Button label="Open" onPress={() => setIsPresented(true)} /> </Host> <BottomSheet isPresented={isPresented} onDismiss={() => setIsPresented(false)} contentPadding={0}> <Column> <Column style={{ backgroundColor: '#0a84ff', padding: 16 }}> <Text textStyle={{ color: '#FFFFFF' }}>This banner reaches the sheet's edge.</Text> </Column> <Button label="Close" onPress={() => setIsPresented(false)} /> </Column> </BottomSheet> </> ); }
Snap points
Pass snapPoints to let the user drag the sheet between multiple resting heights. You can use the semantic values 'half' and 'full' for cross-platform parity. The { fraction } and { height } forms are honored precisely on iOS and web.
When sheet content can be taller than the smallest snap point, wrap it in a ScrollView so the overflow scrolls correctly.
import { useState } from 'react'; import { useColorScheme } from 'react-native'; import { Host, BottomSheet, Button, Column, ScrollView, Text } from '@expo/ui'; export default function BottomSheetSnapPointsExample() { const [isPresented, setIsPresented] = useState(false); const colorScheme = useColorScheme(); const ink = { color: colorScheme === 'dark' ? '#FFFFFF' : '#000000' }; return ( <> <Host matchContents> <Button label="Open" onPress={() => setIsPresented(true)} /> </Host> <BottomSheet isPresented={isPresented} onDismiss={() => setIsPresented(false)} snapPoints={['half', 'full']}> <ScrollView> <Column spacing={12}> <Text textStyle={{ ...ink, fontSize: 20, fontWeight: '700' }}>Half / full sheet</Text> <Text textStyle={ink}>Drag the sheet between half and full screen height.</Text> </Column> </ScrollView> </BottomSheet> </> ); }
On Android,
{ fraction }and{ height }snap to the nearest of'half'/'full'— the underlyingModalBottomSheetonly supports two resting states. The partial state is only visible when content is tall enough to exceed Material's partial threshold; give the content an explicit height or fill the available space if you need the half state on short content.
Scrollable React Native content
The bottom sheet supports a React Native list such as FlatList (or a high-performance list like FlashList or Legend List) as a child when wrapped in RNHostView. snapPoints sizes the sheet, and the list scrolls within that height. With nestedScrollEnabled, the list scrolls its own content first; once it reaches the top edge, the remaining drag moves the sheet.
import { useState } from 'react'; import { FlatList, Text, useColorScheme } from 'react-native'; import { Host, BottomSheet, Button, RNHostView } from '@expo/ui'; const DATA = Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`); export default function BottomSheetScrollableExample() { const [isPresented, setIsPresented] = useState(false); const colorScheme = useColorScheme(); const ink = { color: colorScheme === 'dark' ? '#FFFFFF' : '#000000' }; return ( <> <Host matchContents> <Button label="Open" onPress={() => setIsPresented(true)} /> </Host> <BottomSheet isPresented={isPresented} onDismiss={() => setIsPresented(false)} snapPoints={['half', 'full']}> <RNHostView> <FlatList nestedScrollEnabled style={{ flex: 1 }} data={DATA} keyExtractor={item => item} renderItem={({ item }) => <Text style={[ink, { padding: 16 }]}>{item}</Text>} /> </RNHostView> </BottomSheet> </> ); }
API
import { BottomSheet } from '@expo/ui';
Component
Type: React.Element<BottomSheetProps>
A modal sheet that slides up from the bottom of the screen.
Props for the BottomSheet component, a modal sheet that slides up from the bottom of the screen.
BottomSheetContentPaddingPadding between the sheet and children, in dp on Android, points on iOS, and
CSS pixels on web. Pass 0 for content that reaches the sheet's edges.
When omitted, each platform keeps the inset it applies by default.
Example
``contentPadding={0} — full-bleed content
Example
``contentPadding={{ top: 8, bottom: 24 }} — no horizontal inset
ModifierConfig[]Platform-specific modifier escape hatch. Pass an array of modifier configs
from @expo/ui/swift-ui/modifiers or @expo/ui/jetpack-compose/modifiers.
() => voidCalled when the bottom sheet is dismissed by the user (e.g. swiping down or tapping the overlay).
ColorValueThe color of the scrim overlay rendered behind the bottom sheet.
Pass 'transparent' to make the backdrop invisible while still blocking touches.
boolean • Default: trueWhether pressing the Android hardware back button (or back gesture) dismisses the bottom sheet.
When false, the back press does not dismiss the sheet (note: the press still does not reach
React Native's BackHandler).
boolean • Default: trueWhether tapping the backdrop (scrim) dismisses the bottom sheet.
When false, the sheet stays open until the user explicitly closes it (e.g. via a button).
boolean • Default: trueWhether to show a drag indicator at the top of the sheet.
SnapPoint[]Heights the sheet can rest at.
When omitted, the sheet auto-sizes to its content.
See SnapPoint for the supported values.
Example
``['half', 'full'] — draggable between half and full
Example
``['full'] — always full height
Types
Padding between a BottomSheet and its content — a single value applied to every
edge, or per-edge values where an edge that is left out is 0.
Type: number or object shaped as below:
| Property | Type | Description |
|---|---|---|
| bottom(optional) | number | - |
| left(optional) | number | - |
| right(optional) | number | - |
| top(optional) | number | - |
A snap point describing one of the heights a BottomSheet can rest at.
'half'— Approximately half-screen.'full'— Fully expanded.{ fraction }— A fraction of the screen height (0–1). iOS / web only.{ height }— A fixed pixel height. iOS / web only.
On Android, { fraction } and { height } snap to the nearest of 'half' / 'full'.
See the component docs for platform behavior notes.
Type: 'half' or 'full' or object shaped as below:
| Property | Type | Description |
|---|---|---|
| fraction | number | - |
Or object shaped as below:
| Property | Type | Description |
|---|---|---|
| height | number | - |