The choice
Vaul (by Emil Kowalski, widely adopted at Vercel) is the modern answer for iOS style drawers in React. It's gesture driven, spring animated, accessible through Radix Dialog primitives, and polished to the point that using it feels closer to a native sheet than most web libraries manage. Bottom drawers with numeric snap points are its bread and butter.
swipe-bar aims at the adjacent problem. It's opinionated toward left, right, and bottom sidebars as peers, with multi instance support on the same edge, mouse and touch parity on desktop, and zero runtime dependencies. The feel is similar. The API and scope aren't.
Where each one sits
Vaul is a drawer primitive with a strong bottom sheet identity.
You compose its controlled <Drawer>
elements, pick snap points, and let Radix Dialog take care of
accessibility.
swipe-bar is a sidebar system. Provider plus declarative components, same API for left, right, and bottom, and a context hook for programmatic control. Multi instance and mouse drag are first class concerns.
Feature by feature
Feature comparison between swipe-bar and Vaul
| Feature | @luciodale/swipe-bar Sidebar system, zero deps, multi instance | vaul iOS style drawer built on Radix Dialog |
|---|---|---|
| Edge swipe to open Drag from the screen edge to reveal the panel on mobile. | Supported | Partial |
| Drag to close with velocity commit | Supported | Supported |
| Spring physics | Supported | Supported |
| Left and right side drawers First class side sidebars, same API as bottom. | Supported | Available but bottom-first |
| Bottom sheet with snap points Half-open stop and full-open stop. | mid anchor point | numeric snap array, richer API |
| Multi instance per side Two or more drawers on the same edge, independent state. | Supported | Not supported |
| Mouse drag on desktop Same gesture on pointer devices, not only touch. | Supported | Not supported |
| Nested drawers Push another drawer from inside an open one. | Not supported | Supported |
| Accessibility Focus trap, Escape, aria attributes. | Supported | Radix Dialog backed |
| Runtime dependencies | Zero | Radix Dialog + primitives |
| Production track record | In production at a major bank, hundreds of thousands of users | Adopted at Vercel, battle tested |
| Best fit | Side drawers, multi instance, PWAs. Themeable to match any native look, iOS included. | Bottom sheet with snap points, nested drawer flows. |
- Edge swipe to open
- Yes
- Drag to close with velocity commit
- Yes
- Spring physics
- Yes
- Left and right side drawers
- Yes
- Bottom sheet with snap points
- mid anchor point
- Multi instance per side
- Yes
- Mouse drag on desktop
- Yes
- Nested drawers
- No
- Accessibility
- Yes
- Runtime dependencies
- Zero
- Production track record
- In production at a major bank, hundreds of thousands of users
- Best fit
- Side drawers, multi instance, PWAs. Themeable to match any native look, iOS included.
- Edge swipe to open
- Partial
- Drag to close with velocity commit
- Yes
- Spring physics
- Yes
- Left and right side drawers
- Available but bottom-first
- Bottom sheet with snap points
- numeric snap array, richer API
- Multi instance per side
- No
- Mouse drag on desktop
- No
- Nested drawers
- Yes
- Accessibility
- Radix Dialog backed
- Runtime dependencies
- Radix Dialog + primitives
- Production track record
- Adopted at Vercel, battle tested
- Best fit
- Bottom sheet with snap points, nested drawer flows.
Scope shape, not quality
Both libraries do gesture, physics, and accessibility well. The distinction is intent.
Vaul is optimized for the pattern iOS and Android users recognize as a bottom sheet: numeric snap points, nested push flows, dynamic content height, Radix a11y guarantees. It shines in mobile-first product UX where the bottom sheet is the primary surface.
swipe-bar is optimized for nav and settings drawers that live on the side, with the same gesture grammar also available on the bottom edge. Two settings drawers on the right edge, a nav on the left, and a bottom sheet is a shape swipe-bar models directly.
Multi instance, side by side
swipe-bar treats every sidebar as an independent instance
identified by an id. Opening the nav drawer
doesn't prevent opening a settings drawer from the same edge. The
z-index and overlay stacking stay consistent because the provider
orchestrates it centrally.
<SwipeBarLeft id="nav" isAbsolute>
<Nav />
</SwipeBarLeft>
<SwipeBarLeft
id="settings"
isAbsolute
swipeToOpen={false}
swipeBarZIndex={70}
overlayZIndex={65}
>
<Settings />
</SwipeBarLeft>Vaul treats a drawer as a dialog. Opening two at once on the same edge isn't a pattern the library models natively, and you'd end up coordinating state yourself.
Dependency weight
Vaul depends on Radix Dialog, which itself pulls a small tree of Radix primitives. That's a fair price for the accessibility guarantees and the existing Radix ecosystem familiarity. swipe-bar keeps the bundle lean and implements the accessibility pieces itself. If you already use Radix across your app, Vaul's extra weight is free. If you don't, swipe-bar's zero-dep claim is real.
When to pick Vaul
When to pick swipe-bar
Next steps
- vs shadcn sheet — when you want a modal, not a drawer
- Getting started — install and wire up a sidebar
- Bottom sheets — mid anchor and full sheet patterns