Skip to content

luciodale/swipe-bar

Repository files navigation

swipe-bar logo

swipe-bar

Native swipe sidebars and bottom sheets for React. Zero dependencies.

Documentation  ·  NPM  ·  GitHub

npm version npm downloads bundle size license

Why swipe-bar

Coming from Vaul, shadcn Sheet, react-sliding-pane, or hand-rolled Framer Motion panels, these are the behaviors you get without wiring:

  • Edge swipe to open. Drag from the edge on touch. No bolting gesture libraries on top of a dialog.
  • Drag to close with velocity commit. Flick to dismiss, soft release snaps back. Native iOS and Android drawer feel.
  • Spring physics, no motion library. The animation engine is built in. Zero runtime dependencies.
  • Mouse and touch parity. The same gesture works on desktop pointers, not only mobile.
  • Multi instance per side. Two or more sidebars on the same edge with independent state.
  • Bottom sheet with mid anchor. Half-open stop and full-open stop, like native sheets.
  • Accessible. Focus trap, Escape to close, aria attributes, keyboard nav.

Full comparisons: vs Vaul (same category gesture drawer) · vs shadcn sheet (modal dialog vs drawer)

Install

npm install @luciodale/swipe-bar

Quick Start

import { SwipeBarProvider, SwipeBarLeft } from "@luciodale/swipe-bar";

function App() {
  return (
    <SwipeBarProvider>
      <SwipeBarLeft className="bg-gray-900 text-white">
        <nav>
          <a href="/dashboard">Dashboard</a>
          <a href="/settings">Settings</a>
        </nav>
      </SwipeBarLeft>

      <main>Your app content</main>
    </SwipeBarProvider>
  );
}

Swipe from the left edge on mobile or click the toggle on desktop. That's it.

Features

  • Zero dependencies — just React
  • Left, right, and bottom — all three directions with the same API
  • Native touch gestures — edge swipe detection, drag tracking, velocity commit/cancel
  • Multi-instance — multiple sidebars per direction with independent state via id prop
  • Bottom sheets with mid-anchor — swipe to a halfway stop, then again to fully open
  • Typed sidebar metadata — attach a generic type map and get compile-time safety
  • Programmatic control — open, close, and read state from anywhere via context hook
  • Cross-direction locking — one direction at a time, no gesture conflicts
  • Accessibility — focus trap, Escape to close, aria attributes, keyboard navigation
  • Runtime configuration — change any prop at runtime via setGlobalOptions

Programmatic Control

import { useSwipeBarContext } from "@luciodale/swipe-bar";

function Header() {
  const { openSidebar, closeSidebar, isLeftOpen } = useSwipeBarContext();

  return (
    <header>
      <button onClick={() => openSidebar("left")}>Menu</button>
    </header>
  );
}

Bottom Sheets

import { SwipeBarBottom } from "@luciodale/swipe-bar";

<SwipeBarBottom sidebarHeightPx={400} isAbsolute midAnchorPoint>
  <div>Sheet content</div>
</SwipeBarBottom>

Multi-Instance

Give each sidebar a unique id. Each instance operates independently.

<SwipeBarLeft id="nav" isAbsolute>
  <nav>Navigation</nav>
</SwipeBarLeft>

<SwipeBarLeft
  id="settings"
  isAbsolute
  swipeToOpen={false}
  showToggle={false}
  swipeBarZIndex={70}
  overlayZIndex={65}
>
  <div>Settings panel</div>
</SwipeBarLeft>
const { openSidebar, leftSidebars } = useSwipeBarContext();

openSidebar("left", { id: "settings" });
const isSettingsOpen = leftSidebars.settings?.isOpen ?? false;

Docs

Full documentation, configuration reference, and live examples at koolcodez.com/projects/swipe-bar.

License

MIT

About

Gesture driven React sidebar with spring physics. Touch and mouse support, native app feel for mobile first web interfaces. Tiny bundle, no runtime dependencies.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors