labsUI

Drawer

Edge panel (left, right, top, bottom) that springs in and swipes away. Shares Dialog's focus trap, scroll lock and Esc handling. Sidebar uses it below md.

npx @kznlabs/labsui add drawer
import {  Drawer,  DrawerBody,  DrawerClose,  DrawerContent,  DrawerDescription,  DrawerFooter,  DrawerHeader,  DrawerTitle,  DrawerTrigger,} from "@/components/ui/drawer";export function CartDrawer({ items }: { items: string[] }) {  return (    <Drawer>      <DrawerTrigger variant="outline">Cart</DrawerTrigger>      <DrawerContent side="right">        <DrawerHeader>          <DrawerTitle>Your cart</DrawerTitle>          <DrawerDescription>{items.length} items</DrawerDescription>        </DrawerHeader>        <DrawerBody>          <ul>            {items.map((i) => (              <li key={i}>{i}</li>            ))}          </ul>        </DrawerBody>        <DrawerFooter>          <DrawerClose>Keep shopping</DrawerClose>          <DrawerClose variant="default">Checkout</DrawerClose>        </DrawerFooter>      </DrawerContent>    </Drawer>  );}

<Drawer>

  • children*
    ReactNode

    DialogTrigger and DialogContent.

  • defaultOpendefault false
    boolean

    Open at first render (uncontrolled).

  • onOpenChange
    (open: boolean) => void

    Called on the trigger, Esc, backdrop click or DialogClose.

  • open
    boolean

    Controlled state; pair with onOpenChange.

PropTypeDefault
children*

DialogTrigger and DialogContent.

ReactNode—
defaultOpen

Open at first render (uncontrolled).

booleanfalse
onOpenChange

Called on the trigger, Esc, backdrop click or DialogClose.

(open: boolean) => void—
open

Controlled state; pair with onOpenChange.

boolean—

<DrawerTrigger>

  • children
    ReactNode

    Label; its width is reserved so loading and success never resize the button.

  • confirmLabel
    ReactNode

    Two-step action: first click swaps the label to this, second click runs onClick.

  • confirmTimeout
    number

    Ms before an armed confirm step resets. Also resets on blur or Esc.

  • loading
    boolean

    Disables the button and shows a spinner before the label.

  • loadingLabel
    ReactNode

    Label shown while loading, with the spinner. Defaults to children.

  • size
    "default" | "sm" | "lg" | "icon"

    Height and padding; "icon" is square, so give it an aria-label.

  • status
    ButtonStatus

    In-place feedback: loading spins, success swaps the label to successLabel.

  • successLabel
    ReactNode

    Text shown on status="success", with a check icon. Defaults to "Done".

  • swap
    SwapDirection

    Where the old label slides out to on a swap. Default "left".

  • variant
    "default" | "secondary" | "outline" | "ghost" | "destructive"

    Fill and emphasis: one primary action per view, destructive for irreversible ones.

PropTypeDefault
children

Label; its width is reserved so loading and success never resize the button.

ReactNode—
confirmLabel

Two-step action: first click swaps the label to this, second click runs onClick.

ReactNode—
confirmTimeout

Ms before an armed confirm step resets. Also resets on blur or Esc.

number—
loading

Disables the button and shows a spinner before the label.

boolean—
loadingLabel

Label shown while loading, with the spinner. Defaults to children.

ReactNode—
size

Height and padding; "icon" is square, so give it an aria-label.

"default" | "sm" | "lg" | "icon"—
status

In-place feedback: loading spins, success swaps the label to successLabel.

ButtonStatus—
successLabel

Text shown on status="success", with a check icon. Defaults to "Done".

ReactNode—
swap

Where the old label slides out to on a swap. Default "left".

SwapDirection—
variant

Fill and emphasis: one primary action per view, destructive for irreversible ones.

"default" | "secondary" | "outline" | "ghost" | "destructive"—

Also accepts every Button prop.

<DrawerClose>

  • children
    ReactNode

    Label; its width is reserved so loading and success never resize the button.

  • confirmLabel
    ReactNode

    Two-step action: first click swaps the label to this, second click runs onClick.

  • confirmTimeout
    number

    Ms before an armed confirm step resets. Also resets on blur or Esc.

  • loading
    boolean

    Disables the button and shows a spinner before the label.

  • loadingLabel
    ReactNode

    Label shown while loading, with the spinner. Defaults to children.

  • size
    "default" | "sm" | "lg" | "icon"

    Height and padding; "icon" is square, so give it an aria-label.

  • status
    ButtonStatus

    In-place feedback: loading spins, success swaps the label to successLabel.

  • successLabel
    ReactNode

    Text shown on status="success", with a check icon. Defaults to "Done".

  • swap
    SwapDirection

    Where the old label slides out to on a swap. Default "left".

  • variantdefault "outline"
    "default" | "secondary" | "outline" | "ghost" | "destructive"

    Fill and emphasis: one primary action per view, destructive for irreversible ones.

PropTypeDefault
children

Label; its width is reserved so loading and success never resize the button.

ReactNode—
confirmLabel

Two-step action: first click swaps the label to this, second click runs onClick.

ReactNode—
confirmTimeout

Ms before an armed confirm step resets. Also resets on blur or Esc.

number—
loading

Disables the button and shows a spinner before the label.

boolean—
loadingLabel

Label shown while loading, with the spinner. Defaults to children.

ReactNode—
size

Height and padding; "icon" is square, so give it an aria-label.

"default" | "sm" | "lg" | "icon"—
status

In-place feedback: loading spins, success swaps the label to successLabel.

ButtonStatus—
successLabel

Text shown on status="success", with a check icon. Defaults to "Done".

ReactNode—
swap

Where the old label slides out to on a swap. Default "left".

SwapDirection—
variant

Fill and emphasis: one primary action per view, destructive for irreversible ones.

"default" | "secondary" | "outline" | "ghost" | "destructive""outline"

Also accepts every Button prop.

<DrawerTitle>

Also accepts every <h2> prop (none of its own).

<DrawerDescription>

Also accepts every <p> prop (none of its own).

<DrawerContent>

Panel that slides in from an edge on a spring. Swipe toward the edge to dismiss: side drawers drag from anywhere, top/bottom drawers from the handle or header so their content can scroll.

  • children*
    ReactNode

    Header, body and footer.

  • showClosedefault side === "left" || side === "right"
    boolean

    Close button in the corner. On by default for side drawers.

  • sidedefault "right"
    Side

    Edge it slides in from.

PropTypeDefault
children*

Header, body and footer.

ReactNode—
showClose

Close button in the corner. On by default for side drawers.

booleanside === "left" || side === "right"
side

Edge it slides in from.

Side"right"

<DrawerHeader>

Also a drag handle on top/bottom drawers.

Also accepts every <div> prop (none of its own).

<DrawerBody>

Scrolls when content is taller than the drawer.

Also accepts every <div> prop (none of its own).

<DrawerFooter>

Also accepts every <div> prop (none of its own).

type Side
"left" | "right" | "top" | "bottom"

* required. Every component also takes className.