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

  • SheetTrigger should typically be asChild to preserve semantics (Button, link, icon button).
  • SheetClose can 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

PropTypeDefaultDescription
sideSheetSide"right"Placement of the panel
classNamestringAdditional Tailwind classes
...propsDialogPrimitive.Content propsAll 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 asChild to keep correct semantics for the trigger element.
  • Keep a clear title using SheetTitle for better screen reader navigation.
  • For destructive flows, consider using a modal Dialog instead of a Sheet.

Design guidelines

  • Keep sheets narrow (e.g. w-80w-[420px]) to preserve context.
  • Use right for editing, left for navigation, top/bottom for 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.