tetra-ui Logotetra ui

Bottom Sheet

An interactive bottom sheet built on Expo UI with native snap points, gestures, and keyboard support.

Bottom Sheet is built on @expo/ui native bottom sheets — SwiftUI on iOS and Jetpack Compose on Android. Use it for draggable snap points, scrollable content, keyboard-aware forms, or simple modal sheets.

bottom-sheet

Installation

npx shadcn@latest add @tetra-ui/bottom-sheet

Usage

import { Button, ButtonText } from "@/components/ui/button";
import {
  BottomSheet,
  BottomSheetBody,
  BottomSheetClose,
  BottomSheetContent,
  BottomSheetFooter,
  BottomSheetHeader,
  BottomSheetTitle,
  BottomSheetTrigger,
} from "@/components/ui/bottom-sheet";
<BottomSheet>
  <BottomSheetTrigger asChild>
    <Button>
      <ButtonText>Open</ButtonText>
    </Button>
  </BottomSheetTrigger>
  <BottomSheetContent>
    <BottomSheetHeader>
      <BottomSheetTitle>Bottom Sheet Title</BottomSheetTitle>
    </BottomSheetHeader>
    <BottomSheetBody>
      <Text className="text-foreground">Bottom Sheet Body</Text>
    </BottomSheetBody>
    <BottomSheetFooter>
      <BottomSheetClose asChild>
        <Button>
          <ButtonText>Close</ButtonText>
        </Button>
      </BottomSheetClose>
    </BottomSheetFooter>
  </BottomSheetContent>
</BottomSheet>

Snap points

Pass Expo UI SnapPoint values to BottomSheetContent:

  • "half" — medium detent (iOS) / partial expansion (Android)
  • "full" — large detent (iOS) / full expansion (Android)
  • { fraction: 0.4 } — fractional height
  • { height: 320 } — fixed height in points
<BottomSheetContent snapPoints={[{ fraction: 1 / 3 }, "half", "full"]}>
  {/* ... */}
</BottomSheetContent>

Omit snapPoints to auto-size the sheet to its content.

On iOS, multiple detents are supported natively. On Android, fractional and height snap points map to the nearest partial or expanded state.

Scrollable content

Use BottomSheetScrollView for long lists. Place BottomSheetHeader as a sibling above the scroll view:

<BottomSheetContent snapPoints={["half", "full"]}>
  <BottomSheetHeader>
    <BottomSheetTitle>List</BottomSheetTitle>
  </BottomSheetHeader>
  <BottomSheetScrollView>
    {/* scrollable items */}
  </BottomSheetScrollView>
</BottomSheetContent>

Keyboard

The native sheet handles keyboard avoidance automatically. BottomSheetFooter stays above the keyboard without manual input hooks.

BottomSheetHeader, BottomSheetScrollView, and BottomSheetFooter must be direct children of BottomSheetContent (fragments are fine). On Android, children are split by component type so the footer can sit outside the scroll area in the Compose layout.

<BottomSheetContent snapPoints={["half", "full"]}>
  <BottomSheetHeader>
    <BottomSheetTitle>Send a message</BottomSheetTitle>
  </BottomSheetHeader>
  <BottomSheetScrollView>
    <TextInput placeholder="Message" />
  </BottomSheetScrollView>
  <BottomSheetFooter>
    <Button>Send</Button>
  </BottomSheetFooter>
</BottomSheetContent>

API Reference

BottomSheet

Root open state. Built on Expo UI BottomSheet (SwiftUI / Jetpack Compose). Controlled when open is set.

Prop

Type

BottomSheetTrigger

Extends React Native Pressable props. Opens the sheet on press. Must be used within BottomSheet.

Prop

Type

BottomSheetClose

Extends React Native Pressable props. Closes the sheet on press. Must be used within BottomSheet.

Prop

Type

BottomSheetContent

Extends React Native View props. Hosts the native sheet. Must be used within BottomSheet.

Prop

Type

BottomSheetHeader

Extends React Native View props. Renders children and a trailing close button.

BottomSheetTitle

Extends React Native Text props.

BottomSheetBody

Extends React Native View props. Padded sheet body region.

BottomSheetScrollView

Extends React Native ScrollView props. Sets nestedScrollEnabled and default content padding classes.

BottomSheetFooter

Extends React Native View props. Sticky footer with safe-area and keyboard padding.

Changelog

2026-07-08 - Expo UI rewrite

  • Rebuilt on @expo/ui native bottom sheets (SwiftUI on iOS, Jetpack Compose on Android)
  • Removed BottomSheetPortal, BottomSheetOverlay, useBottomSheet, useBottomSheetAnimation, and useBottomSheetInputHandlers
  • Snap points now use Expo UI SnapPoint format ("half", "full", { fraction }, { height })
  • No portal or overlay required — the native sheet is the modal

2026-06-22 - Keyboard Controller integration

  • Added react-native-keyboard-controller as a dependency for footer keyboard avoidance on iOS.

2026-05-25 - Initial release

  • Compound API: BottomSheet, BottomSheetContent, and layout primitives
  • Snap points, pan-down-to-close, dynamic sizing
  • BottomSheetScrollView, sticky BottomSheetFooter

On this page