Skip to main content
Gremorie

Sheet

Side-anchored panel for longer flows, built on Radix Dialog with directional slide-in from any edge.

Overview

Sheet is a Radix Dialog stylized as a side panel. Use it for content that doesn't deserve full focus the way a Dialog does, but is too rich for a Popover: filter trays, detail panels, multi-section settings, navigation menus on mobile. Slide direction is configurable per side (top, right, bottom, left).

For desktop ergonomics, prefer right (filter and detail panels). left is conventional for navigation. On mobile breakpoints, Drawer is usually a better fit because it adds native drag-to-dismiss gestures.

Preview

Installation

bash npx gremorie@latest add rx-sheet
bash pnpm dlx gremorie@latest add rx-sheet
bash yarn dlx gremorie@latest add rx-sheet
bash bunx --bun gremorie@latest add rx-sheet

Usage

import { Button } from "@gremorie/rx-forms";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@gremorie/rx-overlays";

export function Example() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">Open filters</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Filters</SheetTitle>
          <SheetDescription>
            Narrow down the registry by category and edition.
          </SheetDescription>
        </SheetHeader>
        {/* filter controls */}
      </SheetContent>
    </Sheet>
  );
}

Angular edition planned for Phase 5h. Star the repo to track progress.

API

<Sheet>

Extends Radix Dialog.Root. Same controlled API as Dialog (open, defaultOpen, onOpenChange, modal).

<SheetContent>

PropTypeDefaultDescription
side"top" | "right" | "bottom" | "left""right"Edge the sheet docks against and slides in from.
showCloseButtonbooleantrueRenders the top-right X close button.

Wraps Radix Dialog.Content in a Portal with the overlay. Width and height adapt to side:

  • right / left: full-height, w-3/4 capped at sm:max-w-sm.
  • top / bottom: full-width, h-auto.

<SheetHeader>, <SheetFooter>

Layout containers padded p-4. Header stacks title and description; footer pushes to the bottom (mt-auto) with gap-2.

<SheetTitle>, <SheetDescription>

Map to Radix Dialog.Title and Dialog.Description. Both are required by Radix; render with className="sr-only" if visually hidden.

<SheetTrigger>, <SheetClose>, <SheetOverlay>, <SheetPortal>

Pass-throughs over the Radix primitives with data-slot attributes.

Composition

  1. <Sheet> owns the open/close state.
  2. <SheetTrigger asChild> wraps the focusable element that opens it.
  3. <SheetContent side="right"> mounts via Portal, draws the overlay, slides in from the chosen edge.
  4. Header carries title and description; body holds the main content; footer pins primary actions at the bottom.
  5. Dismissal: click overlay, press Esc, click the X (showCloseButton), or call SheetClose.

Variations

Right filter panel

The canonical desktop pattern. Filter form pinned, scrollable body, apply/reset in the footer.

<Sheet>
  <SheetTrigger asChild>
    <Button variant="outline">Filters</Button>
  </SheetTrigger>
  <SheetContent side="right">
    <SheetHeader>
      <SheetTitle>Filters</SheetTitle>
    </SheetHeader>
    <div className="flex-1 overflow-auto px-4">{/* filter form */}</div>
    <SheetFooter>
      <Button variant="outline">Reset</Button>
      <Button>Apply</Button>
    </SheetFooter>
  </SheetContent>
</Sheet>

Left navigation drawer

Pair with a hamburger trigger on mobile for primary navigation.

<Sheet>
  <SheetTrigger asChild>
    <Button variant="ghost" size="icon" aria-label="Open menu">
      <MenuIcon />
    </Button>
  </SheetTrigger>
  <SheetContent side="left">
    <SheetHeader>
      <SheetTitle>Navigation</SheetTitle>
    </SheetHeader>
    <nav className="px-4">{/* nav links */}</nav>
  </SheetContent>
</Sheet>

Bottom notification panel

Use side="bottom" for transient panels that don't deserve a centered modal (system status, recent activity).

<SheetContent side="bottom">
  <SheetHeader>
    <SheetTitle>Recent activity</SheetTitle>
  </SheetHeader>
  {/* activity feed */}
</SheetContent>

Accessibility

  • Role: role="dialog" with aria-modal="true" (default modal prop).
  • Keyboard: Esc closes; Tab cycles focus within the content; focus is trapped while open.
  • Focus management: focus moves to the first focusable element on open and returns to the trigger on close.
  • Title: SheetTitle is mandatory; without it Radix logs a warning. Use sr-only if you need to hide it visually.
  • Description: links to content via aria-describedby automatically.
  • Reduced motion: slide animations honor prefers-reduced-motion via the underlying Tailwind animate utilities.
  • Drawer - vaul-based bottom sheet with drag-to-dismiss for mobile.
  • Dialog - centered modal for focused decisions.
  • Popover - inline anchored overlay for small contextual UI.
  • Navigation Menu - top-level navigation that doesn't overlay.

On this page