Drawer

GitHub

Drawer panel with an optional trigger, heading, scrollable body, and footer.

Installation

bash
pnpm dlx shadcn@latest add @cs/drawer

Usage

tsx
import { Drawer, DrawerAside } from "@/components/cs/drawer";
tsx
<DrawerAside>
  <main>
    <Drawer
      trigger={<button type="button">Open drawer</button>}
      title="Project settings"
      description="Update how this project is configured."
      size="lg"
      aside
      footer={<button type="button">Save changes</button>}
    >
      <p>Drawer content</p>
    </Drawer>
  </main>
</DrawerAside>

Large aside drawer

Uses the large desktop width while keeping the remaining page content visible.

tsx
import { Button } from "@/components/ui/button"
import { Drawer, DrawerAside } from "@/registry/cs/ui/drawer"

export default function DrawerAsideLargePreview() {
	return (
		<div className="min-h-56 w-full border p-4 ">
			<DrawerAside>
				<p className="text-sm font-medium ">Project members</p>
				<p className="mt-1 text-sm text-muted-foreground">
					The page reserves twice the default drawer width on desktop.
				</p>
				<Drawer
					trigger={<Button className="mt-4">Manage members</Button>}
					title="Project members"
					description="Invite collaborators and set their project roles."
					size="lg"
					aside
					footer={<Button>Save members</Button>}
				>
					<p className="text-sm">Member management controls appear in this panel.</p>
				</Drawer>
			</DrawerAside>
		</div>
	)
}

Aside drawer

Reserves desktop page space so the opened drawer sits beside the content.

tsx
import { Button } from "@/components/ui/button"
import { Drawer, DrawerAside } from "@/registry/cs/ui/drawer"

export default function DrawerAsidePreview() {
	return (
		<div className="min-h-56 w-full border p-4 ">
			<DrawerAside>
				<p className="text-sm font-medium bg-blue-200">Project overview</p>
				<p className="mt-1 text-sm text-muted-foreground bg-blue-200">
					This content remains visible next to the drawer.
				</p>
				<Drawer
					trigger={<Button className="mt-4">Open aside drawer</Button>}
					title="Project settings"
					description="Update how this project is configured."
					aside
				>
					<p className="text-sm">Drawer content is displayed beside the page.</p>
				</Drawer>
			</DrawerAside>
		</div>
	)
}

Large drawer

Uses the large desktop panel width for workflows that need more room.

tsx
import { Button } from "@/components/ui/button";
import { Drawer } from "@/registry/cs/ui/drawer";

export default function DrawerLargePreview() {
  return (
    <Drawer
      trigger={<Button>Open large drawer</Button>}
      title="Project members"
      description="Manage who can access this project."
      size="lg"
      footer={<Button>Save members</Button>}
    >
      <div className="space-y-2 text-sm">
        <p>Invite collaborators and set their project roles.</p>
        <p className="text-muted-foreground">
          The large drawer is twice as wide on desktop screens.
        </p>
      </div>
    </Drawer>
  );
}

Non-modal drawer

Keeps the surrounding page available while the drawer is open.

tsx
import { Button } from "@/components/ui/button";
import { Drawer } from "@/registry/cs/ui/drawer";

export default function DrawerNonModalPreview() {
  return (
    <Drawer
      trigger={<Button variant="outline">Open non-modal drawer</Button>}
      title="Activity"
      description="Review recent project updates without blocking the page."
      modal={false}
    >
      <div className="space-y-2 text-sm">
        <p>Deployment completed successfully.</p>
        <p className="text-muted-foreground">
          You can still interact with the page behind this drawer.
        </p>
      </div>
    </Drawer>
  );
}

API Reference

DrawerContextProps

tsx
type DrawerContextProps = {
	registerAside: (drawer: AsideDrawer) => void
	unregisterAside: (id: string) => void
}

DrawerAside

Reserves page space for the active desktop aside drawer. Wrap the page content that should remain visible next to a Drawer with aside enabled.

Drawer

Drawer panel with an optional trigger, heading, scrollable body, and footer.

Props

NameTypeDefaultDescription
asidebooleanfalseKeeps desktop page content beside a horizontal drawer instead of behind it. Requires a surrounding DrawerAside and always uses non-modal behavior.
childrenReactNode-Content displayed in the scrollable drawer body.
classNamestring-Additional classes for the scrollable drawer body.
descriptionstring-Supporting text displayed below the heading.
footerReactNode-Content displayed below the drawer body.
footerClassNamestring-Additional classes for the drawer footer.
keepMountedboolean-Keeps the drawer content mounted after the drawer closes.
showSwipeHandleboolean-Shows the swipe affordance on touch-capable drawer orientations.
size"md" | "lg""md"Desktop panel width. The large size doubles the default width.
titlestring-Drawer heading.
triggerComponentProps<typeof DrawerTrigger>["render"]-Element rendered as the drawer trigger.