Unified UI

Sheet

A slide-out panel component that appears from any edge of the screen with size options and slot composition.

Basic

A slide-out panel that appears from any edge of the screen. Built on Radix UI with smooth CSS animations, focus trap, and slot-based composition.

Installation

Install the component via the CLI in one command.

npx @work-rjkashyap/unified-ui add sheet
pnpm dlx @work-rjkashyap/unified-ui add sheet
npx @work-rjkashyap/unified-ui add sheet
bunx @work-rjkashyap/unified-ui add sheet

If you haven't initialized your project yet, run npx @work-rjkashyap/unified-ui init first. See the CLI docs for details.

Or install the full package

Use this approach if you prefer to install the entire design system as a dependency instead of copying individual components.

npm install @work-rjkashyap/unified-ui
pnpm add @work-rjkashyap/unified-ui
yarn add @work-rjkashyap/unified-ui
bun add @work-rjkashyap/unified-ui

Anatomy

import {
	Sheet,
	SheetTrigger,
	SheetContent,
	SheetHeader,
	SheetFooter,
	SheetTitle,
	SheetDescription,
	SheetClose,
	Button,
	Input,
} from "@work-rjkashyap/unified-ui";

Basic Usage

The Sheet uses a trigger-based pattern similar to Dialog. Wrap a button in SheetTrigger and the panel content in SheetContent. Radix UI handles open/close state, focus trapping, and scroll locking automatically.

<Sheet>
	<SheetTrigger asChild>
		<Button>Open Sheet</Button>
	</SheetTrigger>
	<SheetContent>
		<SheetHeader>
			<SheetTitle>Panel Title</SheetTitle>
			<SheetDescription>Optional description text.</SheetDescription>
		</SheetHeader>
		<div className="p-6">
			<p>Sheet content goes here.</p>
		</div>
		<SheetFooter>
			<SheetClose asChild>
				<Button variant="secondary">Close</Button>
			</SheetClose>
		</SheetFooter>
	</SheetContent>
</Sheet>

Side

The side prop controls which edge of the screen the sheet slides in from. Four directions are supported.

SideUse Case
rightDetail panels, settings, forms (default)
leftNavigation drawers, sidebars
topSearch bars, announcements, command palettes
bottomAction sheets, mobile menus

Size

The size prop controls the width (for left/right sheets) or height (for top/bottom sheets) of the panel.

<SheetContent side="right" size="sm">   {/* 320px */}
<SheetContent side="right" size="md">   {/* 420px — default */}
<SheetContent side="right" size="lg">   {/* 560px */}
<SheetContent side="right" size="full"> {/* Full width/height */}
SizeWidth (left/right)Height (top/bottom)Use Case
sm320px320pxCompact panels, mobile nav
md420px420pxDefault forms and settings
lg560px560pxComplex forms, data-heavy panels
full100%100%Full-screen overlays

Slot Composition

Sheets use the same slot pattern as Dialog with SheetHeader, content area, and SheetFooter.

<SheetContent>
	{/* Header — title, description, close button */}
	<SheetHeader>
		<SheetTitle>Panel Title</SheetTitle>
		<SheetDescription>Description text</SheetDescription>
	</SheetHeader>

	{/* Body — your custom content (add your own padding) */}
	<div className="flex-1 overflow-y-auto p-6">
		<p>Scrollable content area</p>
	</div>

	{/* Footer — action buttons */}
	<SheetFooter>
		<SheetClose asChild>
			<Button variant="secondary">Cancel</Button>
		</SheetClose>
		<Button variant="primary">Save</Button>
	</SheetFooter>
</SheetContent>

A common pattern for mobile-style navigation sidebars.

<Sheet>
	<SheetTrigger asChild>
		<Button variant="ghost" iconOnly aria-label="Open menu">
			<MenuIcon className="size-4" />
		</Button>
	</SheetTrigger>
	<SheetContent side="left" size="sm">
		<SheetHeader>
			<SheetTitle>Navigation</SheetTitle>
		</SheetHeader>
		<nav className="flex flex-col gap-1 p-4">
			<a href="/" className="px-3 py-2 text-sm rounded-md hover:bg-muted">
				Home
			</a>
			<a
				href="/docs"
				className="px-3 py-2 text-sm rounded-md hover:bg-muted"
			>
				Documentation
			</a>
			<a
				href="/components"
				className="px-3 py-2 text-sm rounded-md hover:bg-muted"
			>
				Components
			</a>
			<a
				href="/examples"
				className="px-3 py-2 text-sm rounded-md hover:bg-muted"
			>
				Examples
			</a>
		</nav>
	</SheetContent>
</Sheet>

Settings Panel Pattern

A detail panel that slides in from the right with a form.

<Sheet>
	<SheetTrigger asChild>
		<Button variant="secondary" iconLeft={<SettingsIcon />}>
			Settings
		</Button>
	</SheetTrigger>
	<SheetContent side="right" size="md">
		<SheetHeader>
			<SheetTitle>Project Settings</SheetTitle>
			<SheetDescription>
				Update your project configuration. Changes are saved when you
				click Save.
			</SheetDescription>
		</SheetHeader>
		<div className="flex-1 overflow-y-auto p-6 flex flex-col gap-4">
			<div className="flex flex-col gap-1.5">
				<label className="text-sm font-medium">Project Name</label>
				<Input placeholder="My Project" />
			</div>
			<div className="flex flex-col gap-1.5">
				<label className="text-sm font-medium">Description</label>
				<Textarea placeholder="What is this project about?" />
			</div>
			<div className="flex flex-col gap-1.5">
				<label className="text-sm font-medium">Visibility</label>
				<Select>
					<SelectTrigger>
						<SelectValue placeholder="Select visibility" />
					</SelectTrigger>
					<SelectContent>
						<SelectItem value="public">Public</SelectItem>
						<SelectItem value="private">Private</SelectItem>
					</SelectContent>
				</Select>
			</div>
		</div>
		<SheetFooter>
			<SheetClose asChild>
				<Button variant="secondary">Cancel</Button>
			</SheetClose>
			<Button variant="primary">Save Changes</Button>
		</SheetFooter>
	</SheetContent>
</Sheet>

Controlled Mode

Use the open and onOpenChange props for programmatic control.

const [open, setOpen] = useState(false);

<Sheet open={open} onOpenChange={setOpen}>
	<SheetTrigger asChild>
		<Button>Open</Button>
	</SheetTrigger>
	<SheetContent>
		<SheetHeader>
			<SheetTitle>Controlled Sheet</SheetTitle>
		</SheetHeader>
		<div className="p-6">
			<p>This sheet is controlled externally.</p>
			<Button
				variant="primary"
				onClick={() => {
					handleSave();
					setOpen(false);
				}}
			>
				Save & Close
			</Button>
		</div>
	</SheetContent>
</Sheet>;

Props

Sheet

PropTypeDefaultDescription
openbooleanControlled open state.
defaultOpenbooleanDefault open state (uncontrolled).
onOpenChange(open: boolean) => voidCallback when the open state changes.
modalbooleantrueWhether the sheet blocks interaction with the rest of the page.

SheetContent

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left""right"Which edge the sheet slides in from.
size"sm" | "md" | "lg" | "full""md"Width (left/right) or height (top/bottom) of the panel.
classNamestringAdditional CSS classes.

SheetHeader

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

SheetFooter

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

SheetTitle

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

SheetDescription

PropTypeDefaultDescription
classNamestringAdditional CSS classes.

SheetTrigger

Uses Radix UI's DialogTrigger API. Use asChild to render the trigger as your own element.

SheetClose

Uses Radix UI's DialogClose API. Use asChild to render the close action as your own element.

Accessibility

Built on @radix-ui/react-dialog (the same primitive that powers the Dialog component) for full ARIA compliance.

  • Focus trap — Focus is contained within the sheet when open (managed by Radix).
  • Focus restoration — Focus returns to the trigger element when the sheet closes.
  • Escape to close — Pressing Escape closes the sheet.
  • Click outside — Clicking the overlay backdrop closes the sheet.
  • Scroll lock — Body scroll is locked while the sheet is open.
  • aria-labelledby — Automatically linked to SheetTitle.
  • aria-describedby — Automatically linked to SheetDescription.
  • The close button has aria-label="Close".
  • Uses z-modal for sheet content and z-overlay for the backdrop.
  • Uses rounded-lg per project guidelines for sheet panels.
  • Slide animations respect prefers-reduced-motion.

Design Tokens

TokenUsage
--surface-raisedSheet panel background
--overlayBackdrop overlay color
--radius-lgSheet border radius (inner edge corners)
--z-modalZ-index for sheet content
--z-overlayZ-index for backdrop
--shadow-lgSheet panel shadow
--duration-normalSlide animation timing
--easing-standardSlide animation easing

On this page