Sheet
Slide-over panel built on Radix Dialog. Use it for side panels, filters, navigation, and contextual actions.
The Sheet component is a slide-over panel (a Dialog variant) used for side panels, filters, mobile navigation, and contextual editing.
It’s built on Radix Dialog and uses PyColors semantic tokens (bg-card, border-border, text-foreground) + the motion utilities already configured in the UI.
Import
import {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
} from "@/components/ui/sheet";Usage
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open sheet</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Title</SheetTitle>
<SheetDescription>Description</SheetDescription>
</SheetHeader>
<div className="mt-6">...</div>
<SheetFooter className="mt-6">
<SheetClose asChild>
<Button variant="secondary">Cancel</Button>
</SheetClose>
<Button>Confirm</Button>
</SheetFooter>
</SheetContent>
</Sheet>"use client";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
} from "@/components/ui/sheet";
export function ProfileSheet() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open sheet</Button>
</SheetTrigger>
<SheetContent side="right" className="w-[380px] sm:w-[420px]">
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>Update your account details. Changes are saved on submit.</SheetDescription>
</SheetHeader>
<div className="mt-6 grid gap-3">
<Input label="Name" placeholder="Patrice" />
<Input label="Email" placeholder="you@domain.com" />
</div>
<SheetFooter className="mt-6">
<SheetClose asChild>
<Button variant="secondary">Cancel</Button>
</SheetClose>
<Button>Save</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}Notes
SheetTriggershould typically beasChildto preserve semantics (Button, link, icon button).SheetClosecan wrap buttons to close without extra state.
Sides
Sheets support 4 placement options via the side prop. Use right for common “edit panels”, and left for navigation.
<SheetContent side="right" />
<SheetContent side="left" />
<SheetContent side="top" />
<SheetContent side="bottom" />import { Sheet, SheetTrigger, SheetContent } from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";
export function SheetSides() {
return (
<div className="flex flex-wrap gap-2">
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Right</Button>
</SheetTrigger>
<SheetContent side="right">...</SheetContent>
</Sheet>
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Left</Button>
</SheetTrigger>
<SheetContent side="left">...</SheetContent>
</Sheet>
</div>
);
}Close behavior
SheetContent renders a top-right close button automatically. Use SheetClose for explicit close actions in the footer.
<SheetClose asChild>
<Button variant="secondary">Cancel</Button>
</SheetClose>import { SheetClose } from "@/components/ui/sheet";
import { Button } from "@/components/ui/button";
export function SheetFooterActions() {
return (
<SheetClose asChild>
<Button variant="secondary">Cancel</Button>
</SheetClose>
);
}API
Props
SheetContent
| Prop | Type | Default | Description |
|---|---|---|---|
side | SheetSide | "right" | Placement of the panel |
className | string | — | Additional Tailwind classes |
...props | DialogPrimitive.Content props | — | All Radix Dialog Content props |
Sheet, SheetTrigger, and SheetClose are re-exports of Radix Dialog primitives and accept their native props.
TypeScript types
export type SheetSide = "top" | "bottom" | "left" | "right";
export interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
side?: SheetSide;
}Accessibility
- Built on Radix Dialog: focus is trapped while open, and focus returns to the trigger on close.
- Close with ESC by default.
- Prefer
SheetTrigger asChildto keep correct semantics for the trigger element. - Keep a clear title using
SheetTitlefor better screen reader navigation. - For destructive flows, consider using a modal Dialog instead of a Sheet.
Design guidelines
- Keep sheets narrow (e.g.
w-80–w-[420px]) to preserve context. - Use
rightfor editing,leftfor navigation,top/bottomfor quick filters. - Avoid nesting multiple scroll containers; prefer a single scrollable content region inside the sheet.
- Use one primary action in the footer to reduce decision friction.