Sheet

A dismissible panel that slides in from an edge.

A filled panel that slides in from an edge. Its free corners are rounded, and it closes from the button, Escape key, or scrim.

tsx
import { Button } from "@/components/ui/button"import {  Sheet,  SheetClose,  SheetContent,  SheetDescription,  SheetFooter,  SheetHeader,  SheetTitle,  SheetTrigger,} from "@/components/ui/sheet"export function Example() {  return (    <Sheet>      <SheetTrigger asChild>        <Button variant="outline">Open</Button>      </SheetTrigger>      <SheetContent>        <SheetHeader>          <SheetTitle>Edit profile</SheetTitle>          <SheetDescription>            Make changes to your profile here. Click save when you&apos;re done.          </SheetDescription>        </SheetHeader>        <div className="grid gap-4 px-4">          <div className="grid gap-1.5">            <label htmlFor="sheet-name" className="text-sm tracking-wide">              Name            </label>            <input              id="sheet-name"              defaultValue="Carol Williams"              className="h-9 w-full rounded-md border border-border bg-transparent px-3 text-sm tracking-wide"            />          </div>          <div className="grid gap-1.5">            <label htmlFor="sheet-username" className="text-sm tracking-wide">              Username            </label>            <input              id="sheet-username"              defaultValue="@peduarte"              className="h-9 w-full rounded-md border border-border bg-transparent px-3 text-sm tracking-wide"            />          </div>        </div>        <SheetFooter>          <Button type="submit" className="transition-colors">            Save changes          </Button>          <SheetClose asChild>            <Button variant="outline" className="transition-colors">              Close            </Button>          </SheetClose>        </SheetFooter>      </SheetContent>    </Sheet>  )}

Installation

npx @arctis-sh/@arctis-sh/ui@latest add 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 variant="outline">Open</Button>  </SheetTrigger>  <SheetContent>    <SheetHeader>      <SheetTitle>Edit profile</SheetTitle>      <SheetDescription>        Update your details, then save.      </SheetDescription>    </SheetHeader>    <SheetFooter>      <Button type="submit">Save changes</Button>      <SheetClose asChild>        <Button variant="outline">Close</Button>      </SheetClose>    </SheetFooter>  </SheetContent></Sheet>

Composition

Sheet
├── SheetTrigger
└── SheetContent
    ├── SheetHeader
    │   ├── SheetTitle
    │   └── SheetDescription
    ├── SheetFooter
    │   └── SheetClose
    └── (close button)

Basic

An edit-profile sheet with fields above and actions in the footer.

tsx
import { Button } from "@/components/ui/button"import {  Sheet,  SheetClose,  SheetContent,  SheetDescription,  SheetFooter,  SheetHeader,  SheetTitle,  SheetTrigger,} from "@/components/ui/sheet"export function Example() {  return (    <Sheet>      <SheetTrigger asChild>        <Button variant="outline">Open</Button>      </SheetTrigger>      <SheetContent>        <SheetHeader>          <SheetTitle>Edit profile</SheetTitle>          <SheetDescription>            Make changes to your profile here. Click save when you&apos;re done.          </SheetDescription>        </SheetHeader>        <div className="grid gap-4 px-4">          <div className="grid gap-1.5">            <label htmlFor="sheet-name" className="text-sm tracking-wide">              Name            </label>            <input              id="sheet-name"              defaultValue="Carol Williams"              className="h-9 w-full rounded-md border border-border bg-transparent px-3 text-sm tracking-wide"            />          </div>          <div className="grid gap-1.5">            <label htmlFor="sheet-username" className="text-sm tracking-wide">              Username            </label>            <input              id="sheet-username"              defaultValue="@peduarte"              className="h-9 w-full rounded-md border border-border bg-transparent px-3 text-sm tracking-wide"            />          </div>        </div>        <SheetFooter>          <Button type="submit" className="transition-colors">            Save changes          </Button>          <SheetClose asChild>            <Button variant="outline" className="transition-colors">              Close            </Button>          </SheetClose>        </SheetFooter>      </SheetContent>    </Sheet>  )}

Side

Set side on SheetContent to top, right, bottom, or left. Each example keeps the content simple and includes footer actions.

tsx
import { Button } from "@/components/ui/button"import {  Sheet,  SheetClose,  SheetContent,  SheetDescription,  SheetFooter,  SheetHeader,  SheetTitle,  SheetTrigger,} from "@/components/ui/sheet"const SIDES = ["top", "right", "bottom", "left"] as constconst sideCopy = {  top: {    title: "What’s new this week",    description:      "A short tour of the updates that landed since your last visit — nothing you have to act on right away.",    body: [      "We’ve shipped a quieter notifications panel, faster search across your workspace, and clearer activity history so you can pick up where you left off without digging through old threads.",      "Shared links now open with the right permission prompt, and comment mentions include a bit more context so you know why someone pulled you in.",      "If something looks off after the update, you can roll individual features back from Settings → Experiments while we keep polishing the defaults.",    ],  },  right: {    title: "Project details",    description:      "Context that stays beside your canvas so you don’t lose place jumping between tabs.",    body: [      "Status, owners, and recent updates stay in view while you edit. Use this panel when you need a quick check without leaving the document you’re already reading.",      "Timeline events are sorted newest first. Expand any row for the full note, attachments, and who made the change — useful when you’re catching up after time away.",      "You can pin this sheet open while you work, or close it anytime. Nothing here edits the file until you choose an action at the bottom.",    ],  },  bottom: {    title: "Confirm before you leave",    description:      "You still have unsaved edits on this page. Take a second before discarding them.",    body: [      "Closing now will drop the draft you’ve been working on, including comments that haven’t been posted yet and any fields you changed in the last few minutes.",      "If you meant to keep going, stay on this page and finish reviewing. When you’re ready, save from the main toolbar — this sheet is only a warning, not a commit.",      "Sure you want to leave? Confirm below and we’ll return you to the previous view without storing this draft.",    ],  },  left: {    title: "Browse the library",    description:      "Collections, filters, and saved views — without covering the canvas you’re editing.",    body: [      "Pick a section here, then keep working on the right. The library stays in one continuous flow so you can jump between references without losing selection in the main editor.",      "Starred items float to the top of each collection. Filters remember the last combination you used in this workspace, so opening the panel again should feel familiar.",      "Need something new? Create a collection from the footer, or close this sheet and search from the command menu if you already know the name.",    ],  },} as constexport function Example() {  return (    <div className="flex flex-wrap gap-2">      {SIDES.map((side) => {        const copy = sideCopy[side]        return (          <Sheet key={side}>            <SheetTrigger asChild>              <Button variant="outline" className="capitalize">                {side}              </Button>            </SheetTrigger>            <SheetContent side={side}>              <SheetHeader>                <SheetTitle>{copy.title}</SheetTitle>                <SheetDescription>{copy.description}</SheetDescription>              </SheetHeader>              <div className="grid gap-3 overflow-y-auto px-4 text-sm tracking-wide text-muted-foreground">                {copy.body.map((paragraph) => (                  <p key={paragraph.slice(0, 24)}>{paragraph}</p>                ))}              </div>              <SheetFooter>                <Button type="button" className="transition-colors">                  Continue                </Button>                <SheetClose asChild>                  <Button variant="outline" className="transition-colors">                    Close                  </Button>                </SheetClose>              </SheetFooter>            </SheetContent>          </Sheet>        )      })}    </div>  )}

No Close Button

Pass showCloseButton={false} to hide the corner close control. The sheet still closes with Escape or the scrim.

tsx
import { Button } from "@/components/ui/button"import {  Sheet,  SheetContent,  SheetDescription,  SheetHeader,  SheetTitle,  SheetTrigger,} from "@/components/ui/sheet"export function Example() {  return (    <Sheet>      <SheetTrigger asChild>        <Button variant="outline">Open Sheet</Button>      </SheetTrigger>      <SheetContent showCloseButton={false}>        <SheetHeader>          <SheetTitle>No close button</SheetTitle>          <SheetDescription>            This sheet hides the default close control. Dismiss with Escape or            by clicking the overlay.          </SheetDescription>        </SheetHeader>      </SheetContent>    </Sheet>  )}

API Reference

Sheet

PropTypeDefault
openboolean
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void
<Sheet>  <SheetTrigger asChild>    <Button variant="outline">Open</Button>  </SheetTrigger>  <SheetContent>    <SheetHeader>      <SheetTitle>Title</SheetTitle>      <SheetDescription>Description</SheetDescription>    </SheetHeader>  </SheetContent></Sheet>

SheetTrigger

PropTypeDefault
asChildbooleanfalse
classNamestring
<SheetTrigger asChild>  <Button variant="outline">Open</Button></SheetTrigger>

SheetClose

PropTypeDefault
asChildbooleanfalse
classNamestring
<SheetClose asChild>  <Button variant="outline">Close</Button></SheetClose>

SheetContent

Filled panel. Radius on the free edge only (opposite side).

PropTypeDefault
side"top" | "right" | "bottom" | "left""right"
showCloseButtonbooleantrue
classNamestring
<SheetContent side="right">  Content</SheetContent>

SheetHeader

PropTypeDefault
classNamestring
<SheetHeader>  <SheetTitle>Title</SheetTitle>  <SheetDescription>Description</SheetDescription></SheetHeader>

SheetFooter

Pinned to the bottom of tall side sheets via mt-auto.

PropTypeDefault
classNamestring
<SheetFooter>  <Button>Save</Button></SheetFooter>

SheetTitle

PropTypeDefault
classNamestring
<SheetTitle>Title</SheetTitle>

SheetDescription

PropTypeDefault
classNamestring
<SheetDescription>Description</SheetDescription>