Sheet
Extends modal dialogs with side drawer panels (left, right, top, bottom) featuring customizable backdrop overlays matching Drawer.
import
import { Sheet } from "@/components/ui/sheet/sheet";$
npx @bloomui-react/cli add sheetsheet.tsx
"use client";
import { Icon } from "@iconify/react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import * as React from "react";
import { cn } from "@/lib/utils";
export type SheetBackdrop = "blur" | "dark" | "light" | "transparent" | "none";
export type SheetSide = "top" | "bottom" | "left" | "right";
const backdropVariants: Record<SheetBackdrop, string> = {
blur: "bg-black/50 backdrop-blur-md",
dark: "bg-black/80 backdrop-blur-xs",
light: "bg-zinc-950/20 backdrop-blur-xs",
transparent: "bg-transparent",
none: "",
};
const Sheet = ({
modal = true,
...props
}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root>) => (
<DialogPrimitive.Root modal={modal} {...props} />
);
Sheet.displayName = "Sheet";
const SheetTrigger = DialogPrimitive.Trigger;
const SheetClose = DialogPrimitive.Close;
const SheetPortal = DialogPrimitive.Portal;
export interface SheetOverlayProps
extends React.ComponentPropsWithoutRef<"div"> {
backdrop?: SheetBackdrop;
}
const SheetOverlay = React.forwardRef<HTMLDivElement, SheetOverlayProps>(
({ className, backdrop = "blur", ...props }, ref) => {
if (backdrop === "none") return null;
return (
<SheetClose asChild>
<div
ref={ref}
className={cn(
"fixed inset-0 z-50 cursor-pointer pointer-events-auto max-w-[110rem] mx-auto left-0 right-0 overflow-hidden transition-all duration-300 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
backdropVariants[backdrop],
className,
)}
{...props}
/>
</SheetClose>
);
},
);
SheetOverlay.displayName = "SheetOverlay";
const positionStyles: Record<SheetSide, string> = {
right:
"absolute top-0 right-0 bottom-0 h-full w-3/4 sm:w-96 rounded-l-3xl border-l border-zinc-200 dark:border-zinc-800 data-[state=open]:slide-in-from-right data-[state=closed]:slide-out-to-right",
left: "absolute top-0 left-0 bottom-0 h-full w-3/4 sm:w-96 rounded-r-3xl border-r border-zinc-200 dark:border-zinc-800 data-[state=open]:slide-in-from-left data-[state=closed]:slide-out-to-left",
bottom:
"absolute bottom-0 left-0 right-0 h-96 rounded-t-3xl border-t border-zinc-200 dark:border-zinc-800 data-[state=open]:slide-in-from-bottom data-[state=closed]:slide-out-to-bottom",
top: "absolute top-0 left-0 right-0 h-96 rounded-b-3xl border-b border-zinc-200 dark:border-zinc-800 data-[state=open]:slide-in-from-top data-[state=closed]:slide-out-to-top",
};
export interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
side?: SheetSide;
backdrop?: SheetBackdrop;
showCloseButton?: boolean;
}
const SheetContent = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Content>,
SheetContentProps
>(
(
{
side = "right",
backdrop = "blur",
showCloseButton = true,
className,
children,
...props
},
ref,
) => (
<SheetPortal>
<SheetOverlay backdrop={backdrop} />
<div className="fixed inset-0 z-50 pointer-events-none max-w-[110rem] mx-auto left-0 right-0 overflow-hidden">
<DialogPrimitive.Content
ref={ref}
className={cn(
"pointer-events-auto relative z-50 flex flex-col bg-white dark:bg-zinc-900 p-6 text-zinc-900 dark:text-zinc-100 shadow-2xl overflow-hidden duration-300 ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out",
positionStyles[side || "right"],
className,
)}
{...props}
>
<div className="flex flex-col h-full overflow-hidden gap-4">
{children}
</div>
{showCloseButton && (
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-lg p-1.5 text-zinc-400 hover:text-zinc-900 dark:hover:text-zinc-100 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-sky-500/20 z-10">
<Icon icon="hugeicons:cancel-01" className="size-5" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</div>
</SheetPortal>
),
);
SheetContent.displayName = DialogPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-left pr-6 shrink-0",
className,
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 gap-2 mt-auto pt-4 border-t border-zinc-100 dark:border-zinc-800/80 shrink-0",
className,
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-bold text-zinc-900 dark:text-zinc-100",
className,
)}
{...props}
/>
));
SheetTitle.displayName = DialogPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-xs text-zinc-500 dark:text-zinc-400", className)}
{...props}
/>
));
SheetDescription.displayName = DialogPrimitive.Description.displayName;
export {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetOverlay,
SheetPortal,
SheetTitle,
SheetTrigger,
};
Default
Standard side drawer panel sliding from the right.
Side Positions
Change panel slide entry direction using the side prop.
side:
top | bottom | left | rightBackdrop Overlays
Custom backdrop overlay variants matching Drawer exactly: blur, dark (80%), light, transparent, and none.
backdrop:
blur | dark | light | transparent | noneAPI Reference
Props — SheetContent
Supported properties for SheetContent.
| Prop | Type | Default | Description |
|---|---|---|---|
| side | 'top' | 'bottom' | 'left' | 'right' | 'right' | Slide entry direction for the sheet panel. |
| backdrop | 'blur' | 'dark' | 'light' | 'transparent' | 'none' | 'blur' | Backdrop overlay style (same as Drawer). |
| showCloseButton | boolean | true | Renders top-right close icon button. |