Skip to content
React NativeBetaMobile only

BottomSheet

A panel that slides up from the bottom for secondary actions, a short form or details. Drag down, tap outside or press back to close.

Beta. React Native support is in beta. APIs may change or break between prereleases (npm tag native).

On this page
Web preview — real device may differ
tsx
import { useState } from "react";
import { Text, TextInput } from "react-native";
import { Button } from "@/component-lib/button";
import { useKitTheme } from "@/component-lib/shared/native";
import { BottomSheet, BottomSheetClose } from "@/component-lib/bottom-sheet";
import { ListItem, List } from "@/component-lib/list-item";
import { Row, Section } from "./layout"; // Section and Row: page layout for the demo
 
export function BottomSheetDemo() {
  const { tokens } = useKitTheme();
  const [open, setOpen] = useState(false);
  const [last, setLast] = useState("nothing yet");
  const [note, setNote] = useState("");
  return (
    <Section title="BottomSheet">
      <Row>
        <BottomSheet
          trigger={<Button variant="outline">Share</Button>}
          title="Share invoice"
          description="Pick how to send INV-2041."
          footer={(close) => (
            <Button variant="secondary" onPress={close}>
              Cancel
            </Button>
          )}
        >
          {(close) => (
            <List style={{ marginHorizontal: -16 }}>
              {["Copy link", "WhatsApp", "Email", "Download PDF"].map((label) => (
                <ListItem
                  key={label}
                  title={label}
                  onPress={() => {
                    setLast(label);
                    close();
                  }}
                />
              ))}
            </List>
          )}
        </BottomSheet>
        <Button variant="outline" onPress={() => setOpen(true)}>
          Add note (controlled)
        </Button>
      </Row>
      <Text style={{ color: tokens.muted, fontSize: tokens.fontSize - 2 }}>Last action: {last}</Text>
      <BottomSheet
        open={open}
        onOpenChange={setOpen}
        title="Add a note"
        footer={
          <>
            <BottomSheetClose>
              <Button variant="ghost">Discard</Button>
            </BottomSheetClose>
            <Button
              onPress={() => {
                setLast(note ? `note "${note}"` : "empty note");
                setOpen(false);
              }}
            >
              Save
            </Button>
          </>
        }
      >
        <TextInput
          value={note}
          onChangeText={setNote}
          placeholder="Type a note; the sheet stays above the keyboard"
          placeholderTextColor={tokens.muted}
          multiline
          style={{
            minHeight: 96,
            borderWidth: 1,
            borderColor: tokens.borderControl,
            borderRadius: tokens.radius,
            padding: 12,
            color: tokens.fg,
            fontSize: tokens.fontSize,
            textAlignVertical: "top",
          }}
        />
      </BottomSheet>
    </Section>
  );
}

Install

npx @grampro/kit@native add BottomSheet

Usage

tsx
import { BottomSheet } from "@/component-lib/bottom-sheet";
 
<BottomSheet
  trigger={<Button variant="outline">Share</Button>}
  title="Share invoice"
  footer={(close) => <Button onPress={close}>Done</Button>}
>
  <ListItem title="Copy link" onPress={copy} />
</BottomSheet>

Props

BottomSheetProps

PropTypeDescription
openboolean
defaultOpenboolean
onOpenChange(open: boolean) => void
triggerReactElement<{ onPress?(event: GestureResponderEvent): void }>Pressing it opens the sheet: a Button or any element taking onPress.
titleReactNodeHeading, also the sheet's accessible name.
descriptionReactNode
footerRenderableSticky actions under the scrolling content. A function receives close.
dismissiblebooleanTap outside, drag down and Android back close it. Default true.
maxHeightnumberLargest height as a fraction of the screen. Default 0.9.
scrollablebooleanContent scrolls when taller than the sheet. Default true.
onClosed() => voidAfter the exit animation.
styleStyleProp<ViewStyle>
stylesPartial<{ body: StyleProp<ViewStyle>; footer: StyleProp<ViewStyle> }>
childrenRenderableContent. A function receives close.

Notes

  • Built on Kit's shared Sheet: clears the home indicator and stays above the keyboard.
  • No gesture or animation library: core Animated and PanResponder.
  • Mobile only: there is no web version of this component.