Sheet

Overlay & Feedback

Bottom sheet overlay with header and content.

Preview

Usage

example.jsx
import { Sheet, SheetHeader, SheetTitle, SheetDescription, SheetContent, SheetFooter } from "@/components/ui/sheet";

export default function Example() {
  return <Sheet />;
}

Source Code

Copy this file into components/ui/sheet.jsx in your project.

sheet.jsx
"use client";

import { forwardRef, useEffect } from "react";
import { cn } from "@/lib/utils";

const Sheet = forwardRef(({ className, open, onClose, side = "right", children, ...props }, ref) => {
  useEffect(() => {
    if (open) document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = ""; };
  }, [open]);

  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50">
      <div className="fixed inset-0 bg-black/50 backdrop-blur-sm" onClick={onClose} />
      <div ref={ref}
        className={cn("fixed z-50 flex flex-col border bg-background shadow-lg transition-transform duration-300",
          side === "right" && "inset-y-0 right-0 h-full w-3/4 max-w-sm border-l",
          side === "left" && "inset-y-0 left-0 h-full w-3/4 max-w-sm border-r",
          side === "top" && "inset-x-0 top-0 border-b",
          side === "bottom" && "inset-x-0 bottom-0 border-t",
          className
        )}
        {...props}
      >
        <button onClick={onClose} className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100">
          <svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /></svg>
        </button>
        {children}
      </div>
    </div>
  );
});
Sheet.displayName = "Sheet";

const SheetHeader = forwardRef(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("flex flex-col space-y-2 p-6 pb-0", className)} {...props} />
));
SheetHeader.displayName = "SheetHeader";

const SheetTitle = forwardRef(({ className, ...props }, ref) => (
  <h2 ref={ref} className={cn("text-lg font-semibold text-foreground", className)} {...props} />
));
SheetTitle.displayName = "SheetTitle";

const SheetDescription = forwardRef(({ className, ...props }, ref) => (
  <p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
));
SheetDescription.displayName = "SheetDescription";

const SheetContent = forwardRef(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("flex-1 overflow-auto p-6", className)} {...props} />
));
SheetContent.displayName = "SheetContent";

const SheetFooter = forwardRef(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("flex flex-col-reverse gap-2 p-6 pt-0 sm:flex-row sm:justify-end", className)} {...props} />
));
SheetFooter.displayName = "SheetFooter";

export { Sheet, SheetHeader, SheetTitle, SheetDescription, SheetContent, SheetFooter };

Quick Install

Make sure you have the cn() utility set up. It requires clsx and tailwind-merge.

npm install clsx tailwind-merge