esc

Type to search...

What is swipe-bar?

Swipe sidebars and bottom sheets that feel native. Zero dependencies, just React. You handle the styling, the library handles the gestures.

Rationale

Most sidebar libraries solve the wrong problem. They give you a styled drawer with an open/close API and call it done. The hard part was never the open/close state. It's the gesture handling: detecting edge swipes vs. scroll, managing drag physics, preventing two sidebars from fighting each other, handling mid-anchor points on bottom sheets, trapping focus for accessibility, and making it all feel native rather than janky.

swipe-bar sits in the space between a CSS drawer and a full design system. It handles the interaction layer that's genuinely painful to get right, and stays out of the way for styling and state management. Your CSS. Your layout. Your logic. The library just makes the gestures work.

  • Zero dependencies — just React. No animation libraries, no gesture systems, no CSS frameworks bundled in
  • Native touch gestures — edge-swipe detection, drag tracking with activation thresholds, velocity-based commit/cancel. Works on touch and mouse
  • Cross-direction locking — opening one sidebar locks the others. No accidental double-opens, no conflicting gestures
  • Bottom sheets with mid-anchor — swipe up to a mid-point, swipe again to fully open. Like iOS Maps. Multi-instance support with independent state for all directions (left, right, bottom)
  • Typed sidebar metadata — attach a generic type map to the context hook. Each sidebar gets its own typed meta, validated at compile time
  • Full accessibility — focus trap, Escape to close, aria attributes, keyboard navigation. All automatic

Installation

terminal
npm install @luciodale/swipe-bar

Quick Start

Let's get a sidebar opening and closing in under a minute.

First, wrap your app in the provider. This is the context that coordinates all sidebars:

App.tsx
import { SwipeBarProvider } from "@luciodale/swipe-bar";

function App() {
  return (
    <SwipeBarProvider>
      <Page />
    </SwipeBarProvider>
  );
}

Now add a left sidebar and a button to open it. The useSwipeBarContext hook gives you everything you need:

Page.tsx
import { SwipeBarLeft, useSwipeBarContext } from "@luciodale/swipe-bar";

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

  return (
    <>
      <SwipeBarLeft className="bg-gray-900 text-white p-6">
        <nav>
          <h2>Menu</h2>
          <a href="/dashboard">Dashboard</a>
          <a href="/settings">Settings</a>
          <button onClick={() => closeSidebar("left")}>Close</button>
        </nav>
      </SwipeBarLeft>

      <main>
        <button onClick={() => openSidebar("left")}>
          {isLeftOpen ? "Sidebar open" : "Open sidebar"}
        </button>
      </main>
    </>
  );
}

That's it. On mobile (below 640px), swiping from the left edge also opens the sidebar. On desktop, the toggle button and your custom button both work. Try adding a SwipeBarRight or a SwipeBarBottom the same way.

Next Steps