Drawer

A drawer component for React.

About

Drawer is built on top of Vaul by emilkowalski_.

Installation

Install the following dependencies:

npm install vaul

Copy and paste the following code into your project.

'use client';
 
import clsx from 'clsx';
import { ComponentProps, ComponentPropsWithoutRef, ElementRef, HTMLAttributes, forwardRef } from 'react';
import { Drawer as DrawerPrimitive } from 'vaul';
import styles from './styles.module.scss';
 
const Drawer = ({ shouldScaleBackground = true, ...props }: ComponentProps<typeof DrawerPrimitive.Root>) => (
    <DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />
);
Drawer.displayName = 'Drawer';
 
const DrawerTrigger = DrawerPrimitive.Trigger;
 
const DrawerPortal = DrawerPrimitive.Portal;
 
const DrawerClose = DrawerPrimitive.Close;
 
const DrawerOverlay = forwardRef<
    ElementRef<typeof DrawerPrimitive.Overlay>,
    ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
>(({ className, ...props }, ref) => (
    <DrawerPrimitive.Overlay ref={ref} className={clsx(styles.drawer__overlay, className)} {...props} />
));
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
 
const DrawerContent = forwardRef<
    ElementRef<typeof DrawerPrimitive.Content>,
    ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
>(({ className, children, ...props }, ref) => (
    <DrawerPortal>
        <DrawerOverlay />
        <DrawerPrimitive.Content ref={ref} className={clsx(styles.drawer__content, className)} {...props}>
            <div className={styles.div} />
            {children}
        </DrawerPrimitive.Content>
    </DrawerPortal>
));
DrawerContent.displayName = 'DrawerContent';
 
const DrawerHeader = ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => (
    <div className={clsx(styles.drawer__header, className)} {...props} />
);
DrawerHeader.displayName = 'DrawerHeader';
 
const DrawerFooter = ({ className, ...props }: HTMLAttributes<HTMLDivElement>) => (
    <div className={clsx(styles.drawer__footer, className)} {...props} />
);
DrawerFooter.displayName = 'DrawerFooter';
 
const DrawerTitle = forwardRef<
    ElementRef<typeof DrawerPrimitive.Title>,
    ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
>(({ className, ...props }, ref) => (
    <DrawerPrimitive.Title ref={ref} className={clsx(styles.drawer__title, className)} {...props} />
));
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
 
const DrawerDescription = forwardRef<
    ElementRef<typeof DrawerPrimitive.Description>,
    ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
>(({ className, ...props }, ref) => (
    <DrawerPrimitive.Description ref={ref} className={clsx(styles.drawer__description, className)} {...props} />
));
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
 
export {
    Drawer,
    DrawerClose,
    DrawerContent,
    DrawerDescription,
    DrawerFooter,
    DrawerHeader,
    DrawerOverlay,
    DrawerPortal,
    DrawerTitle,
    DrawerTrigger,
};

Update the import paths to match your project setup.

Usage

import {
    Drawer,
    DrawerClose,
    DrawerContent,
    DrawerDescription,
    DrawerFooter,
    DrawerHeader,
    DrawerTitle,
    DrawerTrigger,
} from '@/components/ui/drawer';
<Drawer>
    <DrawerTrigger>Open</DrawerTrigger>
    <DrawerContent>
        <DrawerHeader>
            <DrawerTitle>Are you absolutely sure?</DrawerTitle>
            <DrawerDescription>This action cannot be undone.</DrawerDescription>
        </DrawerHeader>
        <DrawerFooter>
            <Button>Submit</Button>
            <DrawerClose>
                <Button variant='outline'>Cancel</Button>
            </DrawerClose>
        </DrawerFooter>
    </DrawerContent>
</Drawer>

Examples

Responsive Dialog

You can combine the Dialog and Drawer components to create a responsive dialog. This renders a Dialog component on desktop and a Drawer on mobile.