Get Started
Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Card
- Checkbox
- Collapsible
- Combobox
- Context Menu
- Date Picker (Lite)
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input Group
- Input OTP (Lite)
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
Extends the Dialog component to display content that complements the main content of the screen.
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
export default function SheetDemo() {
return (
<Sheet>
<SheetTrigger render={<Button variant="outline" />}>Open</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when you're done.
</SheetDescription>
</SheetHeader>
<div class="grid flex-1 auto-rows-min gap-6 px-4">
<div class="grid gap-3">
<Label for="sheet-demo-name">Name</Label>
<Input id="sheet-demo-name" value="Pedro Duarte" />
</div>
<div class="grid gap-3">
<Label for="sheet-demo-username">Username</Label>
<Input id="sheet-demo-username" value="@peduarte" />
</div>
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose render={<Button variant="outline" />}>Close</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
)
}Installation
pnpm dlx shadcnui-hono-jsx@latest add sheetnpx shadcnui-hono-jsx@latest add sheetyarn dlx shadcnui-hono-jsx@latest add sheetbunx shadcnui-hono-jsx@latest add sheetUsage
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"<Sheet>
<SheetTrigger>Open</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Are you absolutely sure?</SheetTitle>
<SheetDescription>This action cannot be undone.</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>Composition
Use the following composition to build a Sheet:
Sheet
├── SheetTrigger
└── SheetContent
├── SheetHeader
│ ├── SheetTitle
│ └── SheetDescription
└── SheetFooterSide
Use the side prop on SheetContent to set the edge of the screen where the sheet appears. Values are top, right, bottom, or left.
import { Button } from "@/components/ui/button"
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
const SHEET_SIDES = ["top", "right", "bottom", "left"] as const
export default function SheetSide() {
return (
<div class="flex flex-wrap gap-2">
{SHEET_SIDES.map((side) => (
<Sheet key={side}>
<SheetTrigger
render={<Button variant="outline" class="capitalize" />}
>
{side}
</SheetTrigger>
<SheetContent
side={side}
class="data-[side=bottom]:max-h-[50vh] data-[side=top]:max-h-[50vh]"
>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>
Make changes to your profile here. Click save when you're
done.
</SheetDescription>
</SheetHeader>
<div class="no-scrollbar overflow-y-auto px-4">
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} class="mb-2 leading-relaxed">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco
laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum
dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
</p>
))}
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose render={<Button variant="outline" />}>
Cancel
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
))}
</div>
)
}No Close Button
Use showCloseButton={false} on SheetContent to hide the close button.
import { Button } from "@/components/ui/button"
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
export default function SheetNoCloseButton() {
return (
<Sheet>
<SheetTrigger render={<Button variant="outline" />}>
Open Sheet
</SheetTrigger>
<SheetContent showCloseButton={false}>
<SheetHeader>
<SheetTitle>No Close Button</SheetTitle>
<SheetDescription>
This sheet doesn't have a close button in the top-right corner.
Click outside to close.
</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>
)
}RTL
To enable RTL support in shadcn/ui, see the RTL configuration guide.
// This example sets dir and uses useTranslation because this site is not RTL.
// In an RTL app you won't need them; see the RTL guide.
import {
type Translations,
useTranslation,
} from "@/components/language-selector"
import { Button } from "@/components/ui/button"
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"
const translations: Translations = {
en: {
dir: "ltr",
values: {
open: "Open",
editProfile: "Edit profile",
description:
"Make changes to your profile here. Click save when you're done.",
name: "Name",
username: "Username",
save: "Save changes",
close: "Close",
},
},
ar: {
dir: "rtl",
values: {
open: "فتح",
editProfile: "تعديل الملف الشخصي",
description:
"قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.",
name: "الاسم",
username: "اسم المستخدم",
save: "حفظ التغييرات",
close: "إغلاق",
},
},
he: {
dir: "rtl",
values: {
open: "פתח",
editProfile: "עריכת פרופיל",
description: "בצע שינויים בפרופיל שלך כאן. לחץ שמור כשתסיים.",
name: "שם",
username: "שם משתמש",
save: "שמור שינויים",
close: "סגור",
},
},
}
export function SheetRtl() {
const { dir, t, language } = useTranslation(translations, "ar")
return (
<Sheet>
<SheetTrigger render={<Button variant="outline" />}>
{t.open}
</SheetTrigger>
<SheetContent
dir={dir}
side={dir === "rtl" ? "left" : "right"}
data-lang={dir === "rtl" ? language : undefined}
>
<SheetHeader>
<SheetTitle>{t.editProfile}</SheetTitle>
<SheetDescription>{t.description}</SheetDescription>
</SheetHeader>
<FieldGroup class="px-4">
<Field>
<FieldLabel for="sheet-rtl-name">{t.name}</FieldLabel>
<Input id="sheet-rtl-name" value="Pedro Duarte" />
</Field>
<Field>
<FieldLabel for="sheet-rtl-username">{t.username}</FieldLabel>
<Input id="sheet-rtl-username" value="peduarte" />
</Field>
</FieldGroup>
<SheetFooter>
<Button type="submit">{t.save}</Button>
<SheetClose render={<Button variant="outline" />}>
{t.close}
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
)
}Notes
- Built on the native
<dialog>with Invoker Commands (command/commandfor): no JavaScript, but requires Baseline 2025 browsers (Chrome 135, Firefox 144, Safari 26.2). - Controlled state (
open,defaultOpen,onOpenChange) is not supported, and the trigger does not reflect the open state (aria-expanded). Triggers and close buttons supportrender, e.g.render={<Button variant="outline" />}. - The overlay is the dialog's
::backdrop(the overlay component renders nothing); closing animates out, but only Chromium keeps the popup and backdrop in the top layer meanwhile (elsewhere the backdrop disappears at once); outside clicks close the dialog only whereclosedbyis supported. - Focus handling is the browser's: after the last control, Tab moves to the browser UI before wrapping (page content stays inert), where Base UI keeps focus inside the popup.
- Accepts
classinstead ofclassName. - Upstream documentation: shadcn/ui Sheet. See also Differences from shadcn/ui.
API Reference
See the Base UI Dialog documentation.