BottomSheet

Present draggable bottom sheets with optional peek handle and collapsed snap point.

BottomSheet is a client component built on vaul that slides a draggable panel up from the bottom of the viewport. Use it for filters, details and short tasks on mobile-first layouts. It supports controlled and uncontrolled use, a peek handle while closed, and an optional collapsed snap point.

Import#

typescript
import { BottomSheet } from "@reactberry/system/blocks";

Usage#

typescript
import { useState } from "react";
import { BottomSheet } from "@reactberry/system/blocks";
import { Box, Button, Text } from "@reactberry/system/elements";

export default function Filters() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <Button variant="default" onClick={() => setIsOpen(true)}>
        Filters
      </Button>
      <BottomSheet open={isOpen} onOpenChange={setIsOpen} title="Filters" peekHeight={0} showCloseButton>
        <Box p="m">
          <Text>Sheet content</Text>
        </Box>
      </BottomSheet>
    </>
  );
}

Examples#

Collapsed snap point#

Set collapsedHeight to add a collapsed snap point; drag the handle up to expand. peekContent is shown at the collapsed point. With showBackdrop={false} the page stays interactive, and showBackdropWhenExpanded adds a backdrop only when expanded.

Explicit close#

Set closeOnBackdropClick={false} and closeOnEscape={false} so the sheet closes only through its buttons or by dragging it down.

API#

BottomSheet#

PropTypeDefaultDescription
children
ReactNode
—

Sheet body, rendered in a scrollable area below the header.

title
ReactNode
—

Header title, also used as the accessible title. Without it, a visually hidden "Bottom sheet" title is rendered.

description
ReactNode
—

Accessible description. It is visually hidden.

peekContent
ReactNode
—

Content shown in the header. With collapsedHeight, it is only shown at the collapsed snap point.

open
boolean
—

Controlled open state.

defaultOpen
boolean
true

Initial open state when uncontrolled.

defaultCollapsed
boolean
false

Opens at the collapsed snap point instead of the expanded one. Requires collapsedHeight.

onOpenChange
((open: boolean) => void)
—

Called with the new open state.

onClose
(() => void)
—

Called when the sheet closes.

peekHeight
number
44

Height in pixels of the fixed handle shown at the bottom of the viewport while closed. 0 hides it.

collapsedHeight
string | number
—

Enables a collapsed snap point at this height. Numbers are pixels.

expandedSnapPoint
string | number
—

Expanded snap point used with collapsedHeight. Defaults to "448px".

height
string
28rem

Fixed panel height, used when neither maxHeight nor collapsedHeight is set.

maxHeight
string
—

When supplied, the panel sizes to its content and is capped at this value instead of taking the fixed height. Ignored when a collapsed snap point is in play (the snap points already drive the height in that mode).

maxWidth
string
30rem

Maximum width of the panel.

zIndex
number
10000

Stacking order of the sheet. The backdrop uses zIndex - 1.

showBackdrop
boolean
true

Renders a backdrop and makes the sheet modal. When false, the page stays interactive and scrollable.

showBackdropWhenExpanded
boolean
false

With showBackdrop={false}, shows a non-interactive backdrop while the sheet is at its expanded snap point.

backdropBlur
string
8px

Backdrop blur. "none", "0" or an empty string disables it.

closeOnBackdropClick
boolean
true

Closes the sheet when the backdrop is clicked.

closeOnEscape
boolean
true

Closes the sheet when Escape is pressed.

showCloseButton
boolean
false

Renders a close button in the header.

children is rendered in a scrollable area below the header. No other props are accepted and nothing is spread.

Keyboard#

KeyAction
Escape

Closes the sheet, unless `closeOnEscape` is `false`.

Accessibility#

  • The panel is a vaul Drawer.Content with Drawer.Title and Drawer.Description. A string title is the accessible title; without title, a visually hidden "Bottom sheet" title is rendered. description is visually hidden and only read by assistive technology.
  • Focus is not moved into the sheet when it opens.
  • The close button has aria-label="Close sheet". The peek handle is a button labelled "Open " + title for a string title, otherwise "Open bottom sheet".

Notes#

  • defaultOpen is true, so an uncontrolled sheet opens on mount. Pass open={false} or defaultOpen={false} to start closed.
  • While closed, a handle button is fixed to the bottom of the viewport unless peekHeight is 0.
  • With collapsedHeight, the sheet is unmounted while closed.
  • Passing maxHeight makes the panel size to its content up to that value instead of using the fixed height. It is ignored when collapsedHeight is set.
  • The sheet can always be dragged down to close, even with closeOnBackdropClick and closeOnEscape set to false.