Skip to the content
BooleanPress UI

Overlay

Sheet

A panel that slides in from an edge of the window for a task or detail that belongs beside the page.

Import

import { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription } from "@booleanpress/ui/sheet"

Usage

A sheet is a modal dialog drawn against an edge. Use it for a form or a detail view that should keep the page visible behind it; use a dialog for a short, centred task. SheetTitle names it and SheetDescription describes it.

import { Button } from "@booleanpress/ui/button"
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@booleanpress/ui/sheet"

export function EditConnection() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">Edit connection</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Edit connection</SheetTitle>
          <SheetDescription>Change how this mailer signs in.</SheetDescription>
        </SheetHeader>
        <SheetFooter>
          <SheetClose asChild>
            <Button>Save</Button>
          </SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  )
}

side chooses the edge: right (the default), left, top or bottom. Left and right sheets are three quarters of the window wide, at most 384 px. The sheet is a flex column: give the part that should scroll flex-1 overflow-y-auto, and the header and footer stay in place. showCloseButton={false} removes the × when the footer offers the ways out. Control it with open and onOpenChange; without a trigger, focus returns to the element that had it, or to returnFocusTo.

Examples

Basic

A form in a right-hand sheet, with Save and Cancel in the footer.

Sides

side on each of the four edges.

Long content

A list taller than the window scrolls; the header and footer stay visible.

Without the ×

showCloseButton={false}: the footer and Escape are the ways out.

Accessibility

Semantics
A div with role="dialog" and aria-modal="true", named by SheetTitle and described by SheetDescription. The rest of the page is hidden from assistive technology while it is open.
Labels
Every sheet needs a SheetTitle; keep it for screen readers with className="sr-only" if the design shows none. The × button's name comes from the provider's close string.
Focus
Focus moves into the sheet when it opens, Tab and Shift+Tab stay inside, and focus returns to the trigger when it closes. Without a trigger it returns to the element that had focus when it opened, or to returnFocusTo when that element is gone.
Known limits
  • One modal at a time: a sheet can open a select or a popover, not a second sheet or dialog.

Keyboard

Keyboard
KeyBehaviour
EscapeCloses the sheet.
TabMoves to the next focusable element inside the sheet, wrapping from the last to the first.
ShiftTabMoves to the previous focusable element, wrapping from the first to the last.

API

Sheet

Renders Radix Sheet.Root and passes it every other prop.

Sheet props
PropTypeDefaultDescription
defaultOpenbooleanWhether it starts open, when it controls itself.
modalbooleanKeep it modal: the rest of the page cannot be reached while it is open. Leave it on.
onOpenChange((open: boolean) => void)Called with true or false when it opens or closes.
openbooleanWhether it is open, when you control it. Pair it with onOpenChange.

SheetTrigger

Renders Radix Sheet.Trigger and passes it every other prop.

SheetTrigger props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.

SheetClose

Renders Radix Sheet.Close and passes it every other prop.

SheetClose props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.

SheetContent

Renders Radix Sheet.Content and passes it every other prop.

SheetContent props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.
deferPointerDownOutsidebooleanWhen true, a 'pointerdown' event outside of the layered element will wait for the interaction's click event before dispatching, allowing third-party code to stop propagation of later events and cancel dismissal.
forceMounttrueUsed to force mounting when more control is needed. Useful when controlling animation with React animation libraries.
onCloseAutoFocus((event: Event) => void)Event handler called when auto-focusing on close. Can be prevented.
onEscapeKeyDown((event: KeyboardEvent) => void)Event handler called when the escape key is down. Can be prevented.
onFocusOutside((event: FocusOutsideEvent) => void)Event handler called when the focus moves outside of the DismissableLayer. Can be prevented.
onInteractOutside((event: FocusOutsideEvent | PointerDownOutsideEvent) => void)Event handler called when an interaction happens outside the DismissableLayer. Specifically, when a pointerdown event happens outside or focus moves outside of it. Can be prevented.
onOpenAutoFocus((event: Event) => void)Event handler called when auto-focusing on open. Can be prevented.
onPointerDownOutside((event: PointerDownOutsideEvent) => void)Event handler called when the a pointerdown event happens outside of the DismissableLayer. Can be prevented.
returnFocusToReturnFocusTargetWhere focus goes on close when the element that opened the overlay no longer exists — for example after the confirmed action deleted the row it sat in.
showCloseButtonbooleantrueRender the × button.
side"left" | "right" | "top" | "bottom"rightThe screen edge it slides from.

SheetHeader

Renders a div and passes it every other prop.

SheetFooter

Renders a div and passes it every other prop.

SheetTitle

Renders Radix Sheet.Title and passes it every other prop.

SheetTitle props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.

SheetDescription

Renders Radix Sheet.Description and passes it every other prop.

SheetDescription props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.

Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.

Data attributes: data-slot="sheet" (Sheet), data-slot="sheet-trigger" (SheetTrigger), data-slot="sheet-close" (SheetClose), data-slot="sheet-content" (SheetContent), data-slot="sheet-header" (SheetHeader), data-slot="sheet-footer" (SheetFooter), data-slot="sheet-title" (SheetTitle), data-slot="sheet-description" (SheetDescription).

Provider strings: close (BooleanUIProvider's strings).

Theming

The panel is a floating surface, like menus and popovers: --popover and --popover-foreground. The left and right sheets are placed with logical edges (start, end), so in a right-to-left page side="right" sits at the left edge; the slide animation still moves along the physical axis.

The tokens its classes read. Change their values in your theme, and it follows.

Theme tokens
TokenUsed for
--backgroundbackground
--foregroundtext
--muted-foregroundtext
--popoverbackground
--popover-foregroundtext