modals /
Sheet / Slide-in Drawer
Bottom sheet on mobile, side panel on desktop. Built with vaul for native-feeling drag interactions.
Preview
Source
tsx
"use client";
import { Drawer } from "vaul";
import { Button } from "@/components/ui/button";
import {
Home,
BarChart2,
Settings,
Users,
HelpCircle,
LogOut,
Menu,
} from "lucide-react";
const navItems = [
{ icon: Home, label: "Dashboard", href: "#" },
{ icon: BarChart2, label: "Analytics", href: "#" },
{ icon: Users, label: "Team", href: "#" },
{ icon: Settings, label: "Settings", href: "#" },
{ icon: HelpCircle, label: "Help & Support", href: "#" },
];
export default function SheetDrawer() {
return (
<div className="flex min-h-[200px] items-center justify-center p-8">
<Drawer.Root>
<Drawer.Trigger asChild>
<Button variant="outline" size="lg" className="gap-2">
<Menu className="h-5 w-5" />
Open navigation panel
</Button>
</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay className="fixed inset-0 z-40 bg-black/50" />
<Drawer.Content className="fixed bottom-0 left-0 right-0 z-50 flex flex-col rounded-t-2xl border-t border-border bg-background focus:outline-none sm:bottom-auto sm:left-auto sm:right-0 sm:top-0 sm:h-full sm:w-72 sm:rounded-none sm:rounded-l-2xl sm:border-t-0 sm:border-l">
{/* Drag handle — visible on mobile only */}
<div className="mx-auto mt-3 h-1.5 w-12 flex-shrink-0 rounded-full bg-border sm:hidden" />
<div className="flex flex-1 flex-col p-6 overflow-y-auto">
{/* Header */}
<div className="mb-6">
<p className="text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-1">
Navigation
</p>
<h2 className="text-lg font-bold text-foreground">My Workspace</h2>
</div>
{/* Nav items */}
<nav className="flex-1 space-y-1">
{navItems.map(({ icon: Icon, label, href }) => (
<a
key={label}
href={href}
className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium text-foreground transition-colors hover:bg-muted hover:text-primary"
>
<Icon className="h-4 w-4 text-muted-foreground shrink-0" />
{label}
</a>
))}
</nav>
{/* Footer */}
<div className="pt-6 border-t border-border mt-6">
<a
href="#"
className="flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium text-red-500 hover:bg-red-500/10 transition-colors"
>
<LogOut className="h-4 w-4 shrink-0" />
Sign out
</a>
</div>
</div>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
</div>
);
} Claude Code Instructions
CLI Install
npx innovations add sheet-drawerWhere to use it
Use this for mobile navigation menus, slide-in filter panels, or detail sidebars.
The vaul Drawer is a bottom sheet on mobile and automatically adapts to a side panel on sm+ breakpoints via CSS positioning.
import { Drawer } from 'vaul';
// <Drawer.Root> <Drawer.Trigger> <Drawer.Portal><Drawer.Overlay /><Drawer.Content>...</Drawer.Content></Drawer.Portal></Drawer.Root>
Add to your root layout or page component — the Portal renders outside the React tree so it always appears above all content.