Gesso
Overlays

Sheet

A panel that docks to an edge of the window, on the raised surface.

A panel that slides in from an edge of the window for a filter set, a settings form, or a detail that should not take the reader off the page. It sits on the same Radix dialog as Dialog, so a scrim dims the page, focus stays inside, and Escape closes it. Dialog is centered and Sheet is docked. The mobile Sidebar is a Sheet.

Usage

import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';

<Sheet>
  <SheetTrigger asChild>
    <Button>Open</Button>
  </SheetTrigger>
  <SheetContent side="right">
    <SheetHeader>
      <SheetTitle>Title</SheetTitle>
      <SheetDescription>Supporting copy.</SheetDescription>
    </SheetHeader>
    <SheetFooter>
      <SheetClose asChild>
        <Button variant="outline">Cancel</Button>
      </SheetClose>
    </SheetFooter>
  </SheetContent>
</Sheet>

SheetHeader and SheetFooter are layout slots with 16px of padding, and the footer is pushed to the bottom of the panel with mt-auto, so a short sheet still ends with its actions at the edge. The header keeps its text clear of the close button, so a long title wraps before it.

Pick an edge

side sets where the panel docks: right by default, or left, top, or bottom. A left or right sheet fills the height and is three quarters of the window wide, up to 384px from the sm breakpoint. A top or bottom sheet fills the width and is as tall as its content, with no maximum, so keep what goes in one short. The panel slides in from its edge, and a border on the side facing the page marks where it ends.

Use right for detail and settings, and left for navigation, which is where the Sidebar puts it.

Controlling it

Pass open and onOpenChange to open a sheet from somewhere else, such as a menu item, a keyboard shortcut, or a button that lives far from the sheet, and leave out SheetTrigger. Focus returns to a SheetTrigger when the sheet closes. A sheet without one has nowhere to send it, so focus is not moved back, and onCloseAutoFocus on SheetContent is where you put it.

A Sheet is modal by default, which is what gives it the scrim, the focus trap, the inert page behind it, and the scroll lock. modal={false} on Sheet turns all four off, and the sheet then docks beside a page that stays usable. Tapping the page behind a non-modal sheet closes it.

Props

Sheet, SheetTrigger, SheetClose, SheetTitle, and SheetDescription are the Radix dialog parts, with open, defaultOpen, onOpenChange, and modal as on Dialog. SheetContent takes the Radix content props, plus these.

PropTypeDefaultWhat it does
side"top" | "right" | "bottom" | "left""right"The edge the panel docks to, and the edge it slides in from.
showCloseButtonbooleantrueShows the X in the top right corner. Turn it off when the sheet has its own close control.

SheetOverlay and SheetPortal are exported for composing your own content, and SheetContent already includes both.

Accessibility

  • The panel has role="dialog". SheetTitle names it and SheetDescription describes it, and each is wired up only when it is rendered, so give a sheet a title even when it is hidden with sr-only, as the Sidebar does.
  • Focus moves into the panel on open, to the first focusable element in it. Tab and Shift+Tab stay inside, and Escape or a click on the scrim closes it. On close, focus goes back to the trigger.
  • With modal on, the rest of the page is hidden from assistive tech and cannot scroll.
  • The close button is a native button named "Close", and its focus ring shows on :focus-visible only. It is 36px square, and 44px on a coarse pointer such as a touch screen, at any width.
  • Under reduced motion the panel and the scrim appear and disappear without animating.
  • A sheet does not scroll its own content. Put a scrolling region inside it when the content can be long, since top and bottom sheets have no maximum height and can grow past the window.
  • The edge border is a real border, so the panel stays outlined in forced colors. The close button keeps a transparent outline on focus that the system paints, so focus stays visible in Windows high contrast.

When to use something else

  • Dialog for a focused decision or a short form that should sit in the middle of the window.
  • Alert dialog or Confirm when the user has to answer before anything else, with no close control and no click-outside dismiss.
  • Drawer on phones, where a bottom sheet you can drag between heights is easier to reach than a docked panel. Sheet is the one to use on a wide screen.
  • Popover when the content belongs to a control and the page behind it should stay usable.

Install

shadcn add @gesso/sheet

On this page