Skip to content

Migrate from vaul

import { Drawer } from 'vaul';
import 'vaul/style.css';
import { Drawer } from 'scrollsheet';
import 'scrollsheet/styles.css';

Drop-in: same Drawer.* namespace, translated onto Sheet.*. Nesting is automatic: no separate NestedRoot.

Open the drawer and click through its sections. Same Drawer.* import, same markup.

vaul API Maps to
snapPoints, activeSnapPoint, direction (all 4 edges), modal={false}, onAnimationEnd, onClose, fadeFromIndex, handleOnly, snapToSequentialPoint, onRelease, preventCycle (on Drawer.Handle), asChild A real feature, 1:1
shouldScaleBackground backgroundEffect; your [data-vaul-drawer-wrapper] is picked up automatically
closeThreshold A real feature; the adapter converts conventions (vaul counts the drag away, scrollsheet counts what remains), so vaul’s 0.25 default keeps its feel. Only live when snapPoints is unset, matching vaul
onOpenStart / onCloseStart onOpenChange (fires at the state flip)
onOpenEnd / onCloseEnd onOpenChangeComplete(open) (fires once the transition visually finishes)
onPointerDownOutside / onInteractOutside with preventDefault() backdropDismissible={false} on Drawer.Root (blocks backdrop tap, keeps drag and Esc); the onEscapeKeyDown version is escapeDismissible={false}

Scrollsheet-native props with no vaul counterpart (backdropDismissible, escapeDismissible, keyboardExpands, onTravel, scrollbar, actionsRef) forward through Drawer.Root untranslated, so one native feature never forces a full conversion to Sheet.*.

data-vaul-drawer, -direction, and -snap-points still land on Drawer.Content; data-vaul-handle on Drawer.Handle, so CSS written against vaul’s selectors keeps matching. Title, Description, and Close get no vaul-branded attribute.