Select UI framework
Get Started
Radix UI
AlertApp Store ButtonAspect RatioBadgeBannerBreadcrumbButton GroupButtonCheckboxCommand MenuCompactButtonDialogDrawerDropdown MenuEmptyFancy ButtonFile UploadHintInput OTPInputItemKbdLabelLinkButtonLoaderPopoverProgress BarProgress CircleRadio GroupRatingSelectSeparatorSkeletonSliderSocial ButtonSwitchTagTextareaToggle GroupToggleTooltip
Component drawer-demo not found in registry.
Installation
Install the following dependencies:
pnpm add @radix-ui/react-dialog lucide-react
Copy and paste the following code into your project.
// @Diar Muradi Drawer v0.0
"use client"
import * as React from "react"
import { tv, type VariantProps } from "@diarmuradi/ui/lib/index"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
// Component identifier names
const DRAWER_ROOT_NAME = "DrawerRoot"
const DRAWER_TRIGGER_NAME = "DrawerTrigger"
const DRAWER_CLOSE_NAME = "DrawerClose"
const DRAWER_OVERLAY_NAME = "DrawerOverlay"
const DRAWER_CONTENT_NAME = "DrawerContent"
const DRAWER_HEADER_NAME = "DrawerHeader"
const DRAWER_TITLE_NAME = "DrawerTitle"
const DRAWER_BODY_NAME = "DrawerBody"
const DRAWER_FOOTER_NAME = "DrawerFooter"
export const drawerVariants = tv({
slots: {
overlay: [
// base
"fixed inset-0 z-50 grid grid-cols-1 place-items-end overflow-hidden backdrop-blur-[10px] bg-foreground/25",
// animation
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
],
content: [
// base
"size-full max-w-[400px] overflow-y-auto",
"border-l border-border bg-background",
// animation
"data-[state=open]:duration-200 data-[state=open]:ease-out data-[state=open]:animate-in",
"data-[state=closed]:duration-200 data-[state=closed]:ease-in data-[state=closed]:animate-out",
"data-[state=open]:slide-in-from-right-full",
"data-[state=closed]:slide-out-to-right-full",
// focus
"focus:outline-none",
],
header: ["flex items-center gap-3 border-b border-border p-5"],
title: ["flex-1 text-sm font-semibold text-foreground"],
body: ["flex-1 p-5"],
footer: ["flex items-center gap-4 border-t border-border p-5"],
closeButton: [
"inline-flex size-8 items-center justify-center rounded-md border border-transparent",
"text-muted-foreground transition-colors duration-200",
"hover:bg-muted hover:text-foreground",
"focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2",
],
},
variants: {
side: {
right: {
overlay: "place-items-end",
content: [
"border-l",
"data-[state=open]:slide-in-from-right-full",
"data-[state=closed]:slide-out-to-right-full",
],
},
left: {
overlay: "place-items-start",
content: [
"border-r border-l-0",
"data-[state=open]:slide-in-from-left-full",
"data-[state=closed]:slide-out-to-left-full",
],
},
},
},
defaultVariants: {
side: "right",
},
})
// Root component
const DrawerRoot = DialogPrimitive.Root
DrawerRoot.displayName = DRAWER_ROOT_NAME
// Trigger component
const DrawerTrigger = DialogPrimitive.Trigger
DrawerTrigger.displayName = DRAWER_TRIGGER_NAME
// Close component
const DrawerClose = DialogPrimitive.Close
DrawerClose.displayName = DRAWER_CLOSE_NAME
// Portal component
const DrawerPortal = DialogPrimitive.Portal
DrawerPortal.displayName = "DrawerPortal"
// Overlay component
const DrawerOverlay = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> &
VariantProps<typeof drawerVariants>
>(({ className, side, ...rest }, forwardedRef) => {
const { overlay } = drawerVariants({ side })
return (
<DialogPrimitive.Overlay
ref={forwardedRef}
className={overlay({ className })}
{...rest}
/>
)
})
DrawerOverlay.displayName = DRAWER_OVERLAY_NAME
// Content component
const DrawerContent = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> &
VariantProps<typeof drawerVariants>
>(({ className, children, side, ...rest }, forwardedRef) => {
const { content } = drawerVariants({ side })
return (
<DrawerPortal>
<DrawerOverlay side={side}>
<DialogPrimitive.Content
ref={forwardedRef}
className={content({ className })}
{...rest}
>
<div className="relative flex size-full flex-col">{children}</div>
</DialogPrimitive.Content>
</DrawerOverlay>
</DrawerPortal>
)
})
DrawerContent.displayName = DRAWER_CONTENT_NAME
// Header component
const DrawerHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & {
showCloseButton?: boolean
} & VariantProps<typeof drawerVariants>
>(({ className, children, showCloseButton = true, ...rest }, forwardedRef) => {
const { header, closeButton } = drawerVariants()
return (
<div ref={forwardedRef} className={header({ className })} {...rest}>
{children}
{showCloseButton && (
<DrawerClose className={closeButton()}>
<X className="size-4" />
<span className="sr-only">Close</span>
</DrawerClose>
)}
</div>
)
})
DrawerHeader.displayName = DRAWER_HEADER_NAME
// Title component
const DrawerTitle = React.forwardRef<
React.ComponentRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> &
VariantProps<typeof drawerVariants>
>(({ className, ...rest }, forwardedRef) => {
const { title } = drawerVariants()
return (
<DialogPrimitive.Title
ref={forwardedRef}
className={title({ className })}
{...rest}
/>
)
})
DrawerTitle.displayName = DRAWER_TITLE_NAME
// Body component
const DrawerBody = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof drawerVariants>
>(({ className, children, ...rest }, forwardedRef) => {
const { body } = drawerVariants()
return (
<div ref={forwardedRef} className={body({ className })} {...rest}>
{children}
</div>
)
})
DrawerBody.displayName = DRAWER_BODY_NAME
// Footer component
const DrawerFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof drawerVariants>
>(({ className, ...rest }, forwardedRef) => {
const { footer } = drawerVariants()
return <div ref={forwardedRef} className={footer({ className })} {...rest} />
})
DrawerFooter.displayName = DRAWER_FOOTER_NAME
export {
DrawerRoot as Root,
DrawerTrigger as Trigger,
DrawerClose as Close,
DrawerContent as Content,
DrawerHeader as Header,
DrawerTitle as Title,
DrawerBody as Body,
DrawerFooter as Footer,
}
Update the import paths to match your project setup.
Usage
import * as Drawer from "@/components/ui/drawer"
export default function Example() {
return (
<Drawer.Root>
<Drawer.Trigger asChild>
<button>Open Drawer</button>
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Drawer Title</Drawer.Title>
</Drawer.Header>
<Drawer.Body>
<p>Drawer content goes here.</p>
</Drawer.Body>
<Drawer.Footer>
<button>Action</button>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>
)
}Examples
Basic Drawer
Component drawer-demo not found in registry.
Left Side Drawer
Component drawer-left not found in registry.
Without Header
Component drawer-without-header not found in registry.