PRIZM 4.0DSTA
All components

Sheet

Edge-anchored sliding panel. Opens from any side.

stableBase UI Dialog

Preview

Code

tsx
import { Button } from "@/components/ui/button";
import {
  Sheet, SheetBody, SheetContent, SheetDescription,
  SheetFooter, SheetHeader, SheetTitle, SheetTrigger,
} from "@/components/ui/sheet";

export function Example() {
  return (
    <div className="flex gap-3">
      {/* Left — navigation drawer */}
      <Sheet>
        <SheetTrigger render={<Button variant="outline">Open from left</Button>} />
        <SheetContent side="left">
          <SheetHeader>
            <SheetTitle>Navigation</SheetTitle>
          </SheetHeader>
          <SheetBody>
            <nav>{/* links */}</nav>
          </SheetBody>
        </SheetContent>
      </Sheet>

      {/* Right — form / settings panel */}
      <Sheet>
        <SheetTrigger render={<Button variant="outline">Open from right</Button>} />
        <SheetContent side="right">
          <SheetHeader>
            <SheetTitle>Edit profile</SheetTitle>
            <SheetDescription>Update your details and save.</SheetDescription>
          </SheetHeader>
          <SheetBody>{/* form fields */}</SheetBody>
          <SheetFooter>
            <Button variant="outline">Cancel</Button>
            <Button>Save</Button>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </div>
  );
}

Props

PropTypeDefaultDescription
openbooleanControlled open state.
defaultOpenbooleanUncontrolled initial open state.
onOpenChange(open: boolean) => voidCalled when the open state changes.

Sub-components

SheetTriggerElement that opens the sheet. Supports `render` for asChild pattern.
SheetContentThe sliding panel. Includes backdrop.
PropTypeDefaultDescription
side"left" | "right" | "top" | "bottom""right"Which edge the sheet slides from.
variant"solid" | "glass""solid"Surface treatment. glass enables liquid-glass refraction (C3 themes only).
showCloseButtonbooleantrueRender the X close button.
SheetHeaderTitle + description region.
SheetTitleRequired for accessibility.
SheetDescriptionDescription text.
SheetBodyScrollable middle region.
SheetFooterActions row at the bottom.
SheetCloseExplicit close trigger.

Built on Base UI Dialog. Supports all four edges. Replaces what was originally a separate Drawer component.

All components also accept standard HTML attributes for their root element (e.g. `id`, `aria-*`, `data-*`, event handlers) and forward `ref` where applicable.

Source

The full implementation of components/ui/sheet.tsx is below — copy it into your project and own it. Some components import the cn helper from lib/utils or other primitives; copy those too.

tsx
"use client";

import { cn } from "@/lib/utils";
import { Dialog as BaseDialog } from "@base-ui-components/react/dialog";
import { X } from "lucide-react";
import type { ComponentPropsWithoutRef, ReactNode } from "react";

export const Sheet = BaseDialog.Root;
export const SheetTrigger = BaseDialog.Trigger;
export const SheetClose = BaseDialog.Close;

const sideClasses = {
  left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm data-[starting-style]:-translate-x-full data-[ending-style]:-translate-x-full",
  right:
    "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm data-[starting-style]:translate-x-full data-[ending-style]:translate-x-full",
  top: "inset-x-0 top-0 w-full border-b data-[starting-style]:-translate-y-full data-[ending-style]:-translate-y-full",
  bottom:
    "inset-x-0 bottom-0 w-full border-t data-[starting-style]:translate-y-full data-[ending-style]:translate-y-full",
} as const;

export function SheetContent({
  className,
  children,
  side = "right",
  variant = "solid",
  showCloseButton = true,
  ...props
}: ComponentPropsWithoutRef<typeof BaseDialog.Popup> & {
  side?: keyof typeof sideClasses;
  variant?: "solid" | "glass";
  showCloseButton?: boolean;
  children?: ReactNode;
}) {
  return (
    <BaseDialog.Portal>
      <BaseDialog.Backdrop
        className={cn(
          "fixed inset-0 z-50 bg-bg/60 backdrop-blur-sm",
          "data-[starting-style]:opacity-0 data-[ending-style]:opacity-0",
          "transition-opacity duration-300",
        )}
      />
      <BaseDialog.Popup
        className={cn(
          "fixed z-50 flex flex-col shadow-lg border-border",
          "transition-transform duration-300",
          variant === "glass" ? "surface-glass-panel" : "bg-surface",
          sideClasses[side],
          className,
        )}
        {...props}
      >
        {children}
        {showCloseButton && (
          <BaseDialog.Close
            className={cn(
              "absolute right-4 top-4 rounded-sm text-fg-muted opacity-70 transition-opacity",
              "hover:opacity-100 focus-visible:outline-1 focus-visible:outline-offset-0 focus-visible:outline-accent",
            )}
            aria-label="Close"
          >
            <X className="h-4 w-4" />
          </BaseDialog.Close>
        )}
      </BaseDialog.Popup>
    </BaseDialog.Portal>
  );
}

export function SheetHeader({ className, ...props }: ComponentPropsWithoutRef<"div">) {
  return (
    <div
      className={cn("flex flex-col gap-1.5 border-b border-border px-6 py-4", className)}
      {...props}
    />
  );
}

export function SheetTitle({
  className,
  ...props
}: ComponentPropsWithoutRef<typeof BaseDialog.Title>) {
  return (
    <BaseDialog.Title
      className={cn("text-lg font-semibold leading-none tracking-tight text-fg", className)}
      {...props}
    />
  );
}

export function SheetDescription({
  className,
  ...props
}: ComponentPropsWithoutRef<typeof BaseDialog.Description>) {
  return <BaseDialog.Description className={cn("text-sm text-fg-muted", className)} {...props} />;
}

export function SheetBody({ className, ...props }: ComponentPropsWithoutRef<"div">) {
  return <div className={cn("flex-1 overflow-y-auto px-6 py-4", className)} {...props} />;
}

export function SheetFooter({ className, ...props }: ComponentPropsWithoutRef<"div">) {
  return (
    <div
      className={cn(
        "flex items-center gap-2 border-t border-border px-6 py-4 sm:justify-end",
        className,
      )}
      {...props}
    />
  );
}

JavaFX

Ships in the PRIZM JavaFX library for thick-client C3 apps as PrizmSheet (extends (controller; overlays a StackPane)). Run the gallery to see it natively.

java
import design.prizm.fx.controls.PrizmSheet;

PrizmSheet(Side side)
PrizmSheet(Side side, Node content)
PropTypeDefaultDescription
setContent(Node) → voidThe panel body; compose it yourself.
setSize(double) → voidPanel width (LEFT/RIGHT) or height (TOP/BOTTOM) in px. Default 360 / 280.
show(StackPane host) → voidOverlay on a full-size host (e.g. the scene root wrapped in a StackPane) and slide in.
hide() → voidSlide out and remove from the host (also on scrim click).

No stock JavaFX equivalent — a controller that overlays a host StackPane with a scrim + a panel sliding from the chosen Side (200ms). Styled by the .prizm-sheet / .prizm-sheet-scrim rules. Mirrors components/ui/sheet.tsx (which covers all four sides).