Sheet
Side-anchored panel (left, right, top, or bottom). Useful for settings and detail drawers.
Examples
RTL
Right-to-left layout support for languages such as Arabic and Hebrew.
Translations are AI-generated for demonstration and may be imperfect.
import { Button } from "@codefast/ui/button";
import { Field, FieldGroup, FieldLabel } from "@codefast/ui/field";
import { Input } from "@codefast/ui/input";
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@codefast/ui/sheet";
import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";
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 asChild>
<Button variant="outline">{t.open}</Button>
</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 className="px-4">
<Field>
<FieldLabel htmlFor="sheet-rtl-name">{t.name}</FieldLabel>
<Input id="sheet-rtl-name" defaultValue="Pedro Duarte" />
</Field>
<Field>
<FieldLabel htmlFor="sheet-rtl-username">{t.username}</FieldLabel>
<Input id="sheet-rtl-username" defaultValue="peduarte" />
</Field>
</FieldGroup>
<SheetFooter>
<Button type="submit">{t.save}</Button>
<SheetClose asChild>
<Button variant="outline">{t.close}</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
);
}
Side
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 "@codefast/ui/button";
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@codefast/ui/sheet";
const SHEET_SIDES = ["top", "right", "bottom", "left"] as const;
export function SheetSide() {
return (
<div className="flex flex-wrap gap-2">
{SHEET_SIDES.map((side) => (
<Sheet key={side}>
<SheetTrigger asChild>
<Button variant="outline" className="capitalize">
{side}
</Button>
</SheetTrigger>
<SheetContent side={side} className="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 className="no-scrollbar overflow-y-auto px-4">
{Array.from({ length: 10 }).map((_, index) => (
<p key={index} className="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 asChild>
<Button variant="outline">Cancel</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
))}
</div>
);
}
Usage
The minimal import and composition — see Examples below for styled, real-world variants.
import { Button } from "@codefast/ui/button";
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@codefast/ui/sheet";
export function SheetUsage() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Edit profile</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>Make changes to your profile here.</SheetDescription>
</SheetHeader>
<SheetFooter>
<SheetClose asChild>
<Button variant="outline">Cancel</Button>
</SheetClose>
<Button>Save changes</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- SheetBody is a codefast addition over upstream Radix — long content scrolls on its own while Header/Footer stay pinned, same as DialogBody.
- Four slide-in edges via side ("top" | "right" | "bottom" | "left", default "right").
- Built on Dialog under the hood — inherits its focus trap and inert-background behaviour.
API reference
Props for each part of the component. All native element props are also forwarded.
Sheet
Root. Manages open state (a Dialog under the hood).
openbooleanThe controlled open state.
defaultOpenbooleanThe open state when initially rendered (uncontrolled).
Default
falseonOpenChange(open: boolean) => voidCalled when the open state changes.
SheetContent
side"top" | "right" | "bottom" | "left"Which edge the panel slides in from.
Default
"right"
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
| Key | Function |
|---|---|
| Space | Opens the sheet when the trigger is focused. |
| Tab | Cycles focus within the sheet (focus is trapped). |
| Esc | Closes the sheet and restores focus to the trigger. |
- A Sheet is a Dialog: focus is trapped and the background is inert while open.
- Always provide a SheetTitle so the panel has an accessible name.
- Use a Sheet for side tasks; use a Drawer (Vaul) for mobile bottom-sheet gestures.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Use for secondary tasks — filters, details, quick edits.
- Anchor navigation sheets to the left, content/detail sheets to the right.
Don’t
- Don’t put a multi-step primary flow in a sheet — use a page.
- Don’t stack a sheet over a dialog.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.