BottomSheet
Pre-alphaThe registry and the CLI are not published yet.Roadmap

BottomSheet

Compound bottom sheet with snap points, gestures and scrollable content.

LT

Linus TorvaldsHelsinki
Share

A

Ada

B

Alan

G

Grace

M

Margaret
Copy link
Save to Files

Installation

npx axiom add bottom-sheet
pnpm dlx axiom add bottom-sheet
yarn dlx axiom add bottom-sheet
bun x axiom add bottom-sheet

Also copies: portal, overlay, tappable, slot, title, icon-button.

Requires the close icon in your icon registry.

The sheet follows the keyboard with react-native-keyboard-controller. It’s a native module: rebuild your development build after installing it, and wrap your app in its KeyboardProvider:

app/_layout.tsx
import { KeyboardProvider } from "react-native-keyboard-controller";

<GestureHandlerRootView style={{ flex: 1 }}>
	<KeyboardProvider>
		<PortalProvider>{/* … */}</PortalProvider>
	</KeyboardProvider>
</GestureHandlerRootView>;

Installs: react-native-reanimated, react-native-worklets, react-native-gesture-handler, react-native-safe-area-context.

The sheet renders in the root PortalHost, and its gestures need GestureHandlerRootView above it. Wrap your root layout once:

app/_layout.tsx
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { PortalHost, PortalProvider } from "@/components/core/portal";

export default function RootLayout() {
	return (
		<GestureHandlerRootView style={{ flex: 1 }}>
			<PortalProvider>
				<Stack />
				<PortalHost />
			</PortalProvider>
		</GestureHandlerRootView>
	);
}

Usage

import { BottomSheet } from "@/components/ui/bottom-sheet";

<BottomSheet.Root>
	<BottomSheet.Trigger asChild>
		<Button>Share</Button>
	</BottomSheet.Trigger>
	<BottomSheet.Content snapPoints={["50%", "90%"]}>
		<BottomSheet.Handle />
		<BottomSheet.Header title="Share" />
		{/* content */}
	</BottomSheet.Content>
</BottomSheet.Root>;

Props

BottomSheet.Root

PropTypeDefaultDescription
openbooleanControlled visibility.
defaultOpenbooleanfalseInitial visibility when the sheet manages its own state.
onOpenChange(open: boolean) => voidCalled when the sheet opens or closes, including by swipe, backdrop press or a back action (Android back button, back gesture with a navigator).
childrenReactNodeTrigger and Content.

BottomSheet.Trigger

PropTypeDefaultDescription
asChildbooleanfalseOpens the sheet from the child element instead of rendering a pressable.
childrenReactNodeThe element that opens the sheet.

BottomSheet.Content

PropTypeDefaultDescription
snapPointsArray<number | string>['content']Heights the sheet rests at, from lowest to highest: points (320), a percentage of the screen ('50%'), or 'content' to size it to its children.
indexnumber0Controlled snap point.
onIndexChange(index: number) => voidCalled when the sheet settles on another snap point.
overlaybooleantrueDims the screen behind the sheet through Overlay. The dimming follows the sheet position.
dismissiblebooleantrueCloses on swipe down, backdrop press and back actions. false for a step the user must complete.
detachedbooleanfalseFloats above the bottom edge with all corners rounded, for short confirmations.
keyboardBehavior'interactive' | 'extend' | 'none''interactive'How the sheet reacts when an input inside it opens the keyboard. interactive rises with the keyboard frame by frame, without going past the top safe area. extend also moves to the highest snap point. none leaves the sheet in place. Closing the sheet dismisses the keyboard.
footerReactNodeFloating actions pinned above the bottom safe area, visible at every snap point.
onDismiss() => voidCalled after the close animation ends.
styleStyleProp<ViewStyle>Extra styles for the sheet surface.

BottomSheet.Handle, BottomSheet.Header, BottomSheet.ScrollView

PartPropsDescription
HandlestyleThe grab bar. Drag it to resize. Announced as “Resize sheet” with up and down actions.
Headertitle, leading, trailing, closeButton, styleTitle row. closeButton adds an IconButton that closes the sheet, with the close icon of your registry.
ScrollView / FlatListsame as React NativeScrollable content that hands the gesture back to the sheet when scrolled to the top.

Use cases

Actions on an item

snapPoints={['content']}: the sheet takes the height it needs and no more.

IMG_0142.HEIC3.1 MB · Today

Share

Duplicate

Delete

<BottomSheet.Content detached>
	<BottomSheet.Handle />
	<FileSummary file={file} />
	<OptionItem label="Share" trailing={<Icon name="share" />} onPress={share} />
	<OptionItem label="Delete" destructive onPress={confirmDelete} />
</BottomSheet.Content>

Short form without leaving the screen

The sheet rises above the keyboard; the main button lives in footer so it stays reachable.

CancelNew listCreate
Trip to Porto
qwertyuiop
<BottomSheet.Content snapPoints={["content"]} keyboardBehavior="extend">
	<BottomSheet.Header
		leading={
			<Button variant="ghost" onPress={close}>
				Cancel
			</Button>
		}
		title="New list"
		trailing={
			<Button variant="ghost" disabled={!name} onPress={create}>
				Create
			</Button>
		}
	/>
	<Input autoFocus value={name} onChangeText={setName} />
</BottomSheet.Content>

Map with a draggable list

Several snap points over content that stays interactive. The list scrolls once the sheet is fully open.

Café Lomi350 m · Open until 18:00
Jardin des Plantes1.2 km
<BottomSheet.Root open>
	<BottomSheet.Content
		snapPoints={[120, "45%", "90%"]}
		overlay={false}
		dismissible={false}
	>
		<BottomSheet.Handle />
		<SearchBar />
		<BottomSheet.FlatList data={places} renderItem={renderPlace} />
	</BottomSheet.Content>
</BottomSheet.Root>

When to use it

Use a sheet when the user needs to act without losing the current screen: pick an option, fill a short form, confirm details. See Mobile design guidelines.