Drawer component for React.
npx @tessl/cli install tessl/npm-vaul@1.1.0Vaul is an unstyled drawer component library for React that serves as a Dialog replacement optimized for tablet and mobile devices. It provides a comprehensive set of components that work together to create smooth, accessible drawer interfaces with advanced features like snap points, fade transitions, drag gestures with velocity-based animations, and accessibility support.
npm install vaulstyle.css available for basic stylingimport { Drawer } from "vaul";For TypeScript projects, you can also import individual types:
import {
Drawer,
useDrawerContext,
type DialogProps,
type ContentProps,
type HandleProps,
type WithFadeFromProps,
type WithoutFadeFromProps,
type DrawerDirection,
type DrawerContextValue
} from "vaul";For CommonJS:
const { Drawer } = require("vaul");import { Drawer } from "vaul";
function MyComponent() {
return (
<Drawer.Root>
<Drawer.Trigger>Open Drawer</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay />
<Drawer.Content>
<Drawer.Handle />
<Drawer.Title>Drawer Title</Drawer.Title>
<Drawer.Description>Drawer content goes here.</Drawer.Description>
<Drawer.Close>Close</Drawer.Close>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
);
}Vaul is built around several key components:
Root, Content, Overlay, Trigger, etc.)The fundamental drawer components that form the building blocks of any drawer interface.
// Main root component that provides context and state management
interface Drawer.Root extends React.Component<DialogProps> {}
// Main drawer content container
interface Drawer.Content extends React.ForwardRefExoticComponent<ContentProps> {}
// Backdrop overlay component
interface Drawer.Overlay extends React.ForwardRefExoticComponent<React.ComponentProps<typeof DialogPrimitive.Overlay>> {}
// Button/element that triggers drawer open
interface Drawer.Trigger extends typeof DialogPrimitive.Trigger {}
// Portal component for rendering drawer outside normal DOM tree
interface Drawer.Portal extends React.Component<React.ComponentProps<typeof DialogPrimitive.Portal> & { container?: HTMLElement }> {}Specialized components for drawer interaction and accessibility.
// Draggable handle component for drawer interaction
interface Drawer.Handle extends React.ForwardRefExoticComponent<HandleProps> {}
// Element that closes the drawer when activated
interface Drawer.Close extends typeof DialogPrimitive.Close {}
// Accessible title element for drawer
interface Drawer.Title extends typeof DialogPrimitive.Title {}
// Accessible description element for drawer
interface Drawer.Description extends typeof DialogPrimitive.Description {}
// Special root component for nested drawers
interface Drawer.NestedRoot extends React.Component<DialogProps> {}Type definitions and interfaces for configuring drawer behavior.
interface DialogProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
defaultOpen?: boolean;
children?: React.ReactNode;
snapPoints?: (number | string)[];
fadeFromIndex?: number;
activeSnapPoint?: number | string | null;
setActiveSnapPoint?: (snapPoint: number | string | null) => void;
closeThreshold?: number;
noBodyStyles?: boolean;
shouldScaleBackground?: boolean;
setBackgroundColorOnScale?: boolean;
scrollLockTimeout?: number;
fixed?: boolean;
handleOnly?: boolean;
dismissible?: boolean;
onDrag?: (event: any, percentageDragged: number) => void;
onRelease?: (event: any, open: boolean) => void;
modal?: boolean;
nested?: boolean;
onClose?: () => void;
direction?: 'top' | 'bottom' | 'left' | 'right';
disablePreventScroll?: boolean;
repositionInputs?: boolean;
snapToSequentialPoint?: boolean;
container?: HTMLElement | null;
onAnimationEnd?: (open: boolean) => void;
preventScrollRestoration?: boolean;
autoFocus?: boolean;
}Advanced context and hooks for building custom drawer components and accessing drawer state.
// Hook to access drawer context from within drawer components
function useDrawerContext(): DrawerContextValue;
// Context value interface containing all drawer state and methods
interface DrawerContextValue {
drawerRef: React.RefObject<HTMLDivElement>;
overlayRef: React.RefObject<HTMLDivElement>;
onPress: (event: React.PointerEvent<HTMLDivElement>) => void;
onRelease: (event: React.PointerEvent<HTMLDivElement> | null) => void;
onDrag: (event: React.PointerEvent<HTMLDivElement>) => void;
dismissible: boolean;
isOpen: boolean;
isDragging: boolean;
modal: boolean;
shouldFade: boolean;
activeSnapPoint?: number | string | null;
setActiveSnapPoint: (snapPoint: number | string | null) => void;
closeDrawer: () => void;
direction: DrawerDirection;
// ... additional properties for complete drawer state
}See Configuration & Types for complete context interface and usage examples.