React Offcanvas: The No-Nonsense Guide to Side Panels, Slide-Out Menus & Overlay Navigation






React Offcanvas: Build Side Panels & Slide-Out Menus Fast







React Offcanvas: The No-Nonsense Guide to Side Panels, Slide-Out Menus & Overlay Navigation

Published: June 2025  ·  Reading time: ~10 min  ·  Level: Beginner–Intermediate

Why Bother with react-offcanvas at All?

If you’ve ever tried to hand-roll a React side panel from scratch, you already know the ritual:
thirty minutes of confident coding, followed by two hours of wrestling with
z-index, CSS transition timing functions, and the special kind of rage that only
a runaway overflow: hidden on a parent element can produce.
The react-offcanvas package
exists precisely to spare you that experience.

At its core, react-offcanvas is a lightweight, dependency-lean React component library
for rendering off-canvas panels — UI elements that live outside the visible viewport and slide
into view on demand. Think mobile navigation drawers, filter sidebars in e-commerce apps,
notification trays, or contextual help panels. These patterns appear in almost every serious
web application today, and react-offcanvas provides the structural scaffolding so you can focus
on what goes inside the panel, not on how to make it slide reliably on Chrome, Firefox,
Safari, and that one Android WebView your QA team keeps finding bugs in.

The library is also refreshingly small. It doesn’t ship Bootstrap, doesn’t assume your CSS methodology,
and doesn’t force opinions about state management. It hands you a React panel component
that accepts a handful of well-named props, and then gets out of your way. For teams already
running lean bundles, that matters. For solo developers prototyping fast, it matters even more.

react-offcanvas Installation: From Zero to First Render

Getting react-offcanvas installed
takes about forty-five seconds — the kind of installation experience that should be the norm
but somehow still feels like a small victory. Open your terminal inside any React project
(Create React App, Vite, Next.js, or a custom Webpack setup all work fine) and run one of the following:

# npm
npm install react-offcanvas

# yarn
yarn add react-offcanvas

# pnpm
pnpm add react-offcanvas

Once the package is installed, the react-offcanvas setup requires exactly two imports
in the file where you want to render the panel. First, the component itself; second, the bundled
stylesheet that handles the base transitions and positioning logic. Without that CSS import,
the panel will technically render, but it’ll look like it was designed by someone who had
never seen a monitor.

import Offcanvas from 'react-offcanvas';
import 'react-offcanvas/dist/react-offcanvas.css';

That’s the entire dependency surface. No peer requirements beyond React itself (version 16.3+
is sufficient), no global store, no provider component wrapping your app tree.
The react-offcanvas getting started path is genuinely that flat,
which makes it particularly well-suited to projects where you need to ship a working
React slide-out menu today rather than architect one for next quarter.

Building Your First react-offcanvas Component

The mental model for react-offcanvas is straightforward: you control visibility through
a boolean state variable, you pass that variable to the isMenuOpened prop,
and the component handles the rest. No imperative show() or hide()
method calls, no refs, no DOM manipulation. It’s idiomatic React — controlled component
pattern, top-to-bottom data flow, exactly as expected.

Here’s a minimal but complete react-offcanvas example that renders
a left-side navigation panel toggled by a button:

import React, { useState } from 'react';
import Offcanvas from 'react-offcanvas';
import 'react-offcanvas/dist/react-offcanvas.css';

export default function App() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>
        Open Side Panel
      </button>

      <Offcanvas
        isMenuOpened={isOpen}
        position="left"
        effect="push"
      >
        <nav>
          <h2>Navigation</h2>
          <ul>
            <li><a href="/home">Home</a></li>
            <li><a href="/about">About</a></li>
            <li><a href="/contact">Contact</a></li>
          </ul>
          <button onClick={() => setIsOpen(false)}>
            Close
          </button>
        </nav>
      </Offcanvas>
    </div>
  );
}

Notice that the close action is just setIsOpen(false) — the same state setter
used to open the panel, called from a button placed inside the panel content.
This is intentional: react-offcanvas doesn’t dictate how or where you put the close trigger.
You could close it on overlay click, on route change, on a keyboard event, or after
a successful form submission. The component is agnostic; your application logic owns the state.

The effect="push" prop tells the component to slide the main page content aside
as the panel opens — a classic UX pattern that preserves spatial context for the user.
Alternatively, setting effect="overlay" renders the panel above the page content
without displacing it, which works better for narrower panels or secondary utility drawers
where interrupting the main layout would feel heavy-handed.

react-offcanvas Positioning: Left, Right, Top, and Bottom

One of the underappreciated strengths of react-offcanvas is its flexible positioning system.
The position prop accepts four values — "left", "right",
"top", and "bottom" — and the library handles the corresponding
CSS transform directions automatically. In practice, left and right
are used for React side navigation and menu drawers; top and
bottom suit notification bars, cookie banners, or mobile action sheets.

Switching positions is a one-prop change, which makes A/B testing your navigation UX
genuinely painless. Want to try a right-hand panel for your international audience
who reads right-to-left? Change position="left" to position="right".
No CSS file edits, no transform recalculations, no hunting for the magic pixel offset
that makes the panel sit flush against the viewport edge. The library abstracts all of that.

For applications with multiple panels — say, a left navigation drawer and
a right contextual help panel — you simply render two <Offcanvas> instances
with separate state variables controlling each. They operate independently,
and the library handles stacking correctly as long as you’re sensible about z-index
in your custom styles. A quick react-offcanvas tutorial best practice:
assign different className props to each instance so you can target them
individually in your stylesheet without selector conflicts.

react-offcanvas Customization: Making It Look Like Yours

Out of the box, react-offcanvas ships with functional but minimal default styles —
exactly the right call for a component library that doesn’t want to fight your design system.
React offcanvas customization happens through a combination of the
className prop on the component and CSS overrides targeting the library’s
class names. The main wrapper uses .off-canvas-component,
the content area uses .off-canvas-component__inner,
and the overlay uses .off-canvas-overlay.

/* Custom width and background for your side panel */
.my-side-panel .off-canvas-component__inner {
  width: 320px;
  background: #1e1e2e;
  color: #cdd6f4;
  padding: 2rem;
  box-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);
}

/* Darken the overlay */
.my-side-panel .off-canvas-overlay {
  background: rgba(0, 0, 0, 0.65);
}

Then pass your class name to the component:
<Offcanvas className="my-side-panel" ...>.
This scoping pattern keeps your custom styles from leaking into other Offcanvas instances
on the same page and avoids the specificity arms race that haunts projects where
global CSS overrides accumulate over months. If you’re using CSS Modules or styled-components,
the same principle applies — scope the override to the specific instance’s generated class.

For teams using Tailwind CSS, the recommended approach is to set
the panel’s inner content width and background via Tailwind utility classes applied
directly to the children you render inside the <Offcanvas> wrapper,
then use a thin CSS override just for the .off-canvas-component__inner
container dimensions. This hybrid approach plays cleanly with Tailwind’s JIT compiler
and avoids the need for @apply hacks or arbitrary value proliferation.
The result is a fully styled React overlay panel that matches your
design tokens without friction.

Handling State, Overlay Clicks, and Keyboard Accessibility

A common requirement that the basic example doesn’t cover is closing the panel
when the user clicks the overlay — the darkened area behind the open panel.
react-offcanvas doesn’t fire a built-in callback for this, which sounds like a limitation
until you realize the solution is two lines: render a click handler on a wrapper div
that sits beneath the Offcanvas component, or use the library’s onMenuOpened
and onMenuClosed callback props to synchronize your state with the
component’s internal transitions.

<Offcanvas
  isMenuOpened={isOpen}
  position="right"
  effect="overlay"
  onMenuOpened={() => console.log('Panel opened')}
  onMenuClosed={() => setIsOpen(false)}
>
  {/* panel content */}
</Offcanvas>

Keyboard accessibility deserves deliberate attention here. A React side menu
that opens on button click should also close when the user presses Escape,
and focus should move into the panel when it opens and return to the trigger button
when it closes. Neither behavior ships automatically with react-offcanvas —
but both are straightforward to implement with a useEffect listening for
the keydown event and a useRef pointing at the trigger button
for focus restoration.

Adding aria-expanded={isOpen} to your trigger button and
aria-hidden={!isOpen} to the panel wrapper gives screen readers
the context they need without any additional libraries. These two attributes,
combined with the keyboard handler, bring the component to WCAG 2.1 AA conformance
for interactive disclosure patterns — which is the minimum bar for any production
React side navigation shipped to a general audience.

Real-World react-offcanvas Patterns Worth Stealing

Beyond the standard navigation drawer, react-offcanvas slots cleanly into
several high-value UI patterns that appear repeatedly in production applications.
Understanding these patterns by name makes it easier to communicate design intent
with your team and to search for implementation precedents when you hit edge cases.

  • Filter sidebar — common in e-commerce and data-heavy dashboards.
    The panel opens from the left, contains checkbox groups and range sliders,
    and applies filters to the main content on close or on each change.
    The effect="overlay" setting is ideal here because
    the main content should stay visible for reference.
  • Cart drawer — a right-side panel that slides in when a user
    adds a product to their shopping cart, shows the cart summary,
    and provides a checkout CTA. This pattern converts better than a redirect
    to a dedicated cart page in many A/B tests, and react-offcanvas handles
    the slide-in animation smoothly enough for production use without additional
    animation libraries.
  • Contextual help panel — a narrow right-side drawer triggered
    by a “?” icon in complex forms or multi-step workflows. It displays
    inline documentation without navigating the user away from the task.
    Width customization (width: 280px on the inner container)
    keeps the main UI usable even with the panel open.

Each of these patterns benefits from the same foundational setup:
a single boolean state variable, the <Offcanvas> component
with appropriate position and effect props, and CSS scoped to the specific instance.
The library’s simplicity is its feature, not its limitation —
it’s a chassis, not a finished vehicle, and that’s exactly the right abstraction
level for a React panel component that needs to fit
into dozens of different design systems.

react-offcanvas vs. Building a Custom Side Panel from Scratch

This question comes up on Reddit and Stack Overflow with reliable frequency,
and the honest answer is: it depends on how much custom behavior you need
and how much time you’re willing to invest in CSS transitions.
A hand-rolled React slide-out menu gives you total control —
you own every pixel, every timing function, every z-index decision.
But “total control” in CSS often means “total responsibility for debugging
that one Safari bug that only appears on iPhone 12 with 120Hz refresh rate enabled.”

react-offcanvas is the right call when your requirements are standard:
a panel that slides in from one edge, sits above or pushes the main content,
and closes on a state change. It’s the wrong call when you need multi-step
animated transitions, gesture-driven drag-to-close behavior, or complex
nested panel stacking. In those cases, you’re better served by
Floating UI,
Framer Motion
with a custom panel component, or — if you’re already using it —
the offcanvas component built into Headless UI or Material UI.

For the vast majority of React applications in the real world, though,
the requirements are standard. A navigation drawer, a filter panel,
a cart sidebar — these are solved problems, and reaching for a focused,
lightweight library like react-offcanvas over a 300-line custom implementation
is a defensible engineering decision. It reduces surface area for bugs,
makes the codebase easier for new contributors to understand,
and lets you ship the feature in an afternoon rather than a sprint.

Pro tip: If you’re evaluating react-offcanvas for a production project,
check the library’s GitHub repository for open issues and last commit date before committing.
Lightweight libraries with small maintenance surfaces can go dormant.
At time of writing, the package is stable and actively used —
but due diligence costs nothing and saves considerable pain downstream.

Complete Props Reference

react-offcanvas keeps its API surface intentionally small.
Understanding every available prop takes about three minutes,
which is the right cognitive load for a utility component you want
to grab, configure, and move on from.

  • isMenuOpened (boolean, required) — Controls panel visibility. Pass your state variable here.
  • position (string) — One of "left", "right", "top", "bottom". Defaults to "left".
  • effect (string)"push" displaces main content; "overlay" layers over it. Defaults to "push".
  • width (number) — Panel width in pixels for left/right positions. Defaults to 280.
  • height (number) — Panel height in pixels for top/bottom positions.
  • className (string) — Custom CSS class applied to the root element for scoped style overrides.
  • onMenuOpened (function) — Callback fired when the open animation completes.
  • onMenuClosed (function) — Callback fired when the close animation completes.

The width prop deserves a special mention: it accepts a raw number
(pixels, no unit string), and it’s separate from any CSS width override you might apply.
Set it in the prop for left/right panels to ensure the push effect calculates
the correct transform offset for the main content displacement.
If you set only a CSS width without the prop, the push effect will use
the default value and the math will be wrong — the most common source of
“the panel opens but the page content only shifts a little bit” bug reports.

For react-offcanvas positioning on responsive layouts,
consider managing the position prop dynamically based on a
window.innerWidth check inside a useEffect with a resize listener,
or better yet, via a useMediaQuery hook from
usehooks-ts.
This lets the same <Offcanvas> instance render as a bottom sheet
on mobile and a left sidebar on desktop — a common progressive enhancement
in responsive navigation design.

Next Steps and Further Learning

Once you’re comfortable with the basics of react-offcanvas setup and side panel building,
the natural next step is integrating the panel with your application’s routing layer.
In React Router v6, you can trigger the panel open state in response to a specific route
parameter or a query string flag, making the panel state URL-serializable and therefore
shareable and navigable via browser history. This is the pattern used by Figma,
Linear, and similar single-page applications where UI state is reflected in the URL
for deep-linking purposes.

You should also explore pairing react-offcanvas with a global state manager
if your panel needs to be triggered from multiple unrelated parts of the component tree.
A single isPanelOpen atom in Zustand or Jotai,
or a context value if you prefer staying framework-native,
lets any component in the tree open or close the panel without prop drilling.
This pattern scales cleanly and avoids the “lifted state that ended up in App.jsx”
antipattern that haunts React codebases as they grow.

Finally, consider the performance angle: react-offcanvas renders its children
even when the panel is closed (it uses CSS transforms to move the panel off-screen
rather than conditional rendering). For panels with expensive child components —
large lists, charts, or data tables — wrap the children in a conditional render
or a React.lazy boundary so they only mount when the panel is actually open.
This is a micro-optimization in most cases, but in low-powered mobile devices
or panels with heavy virtualized lists, it makes a measurable difference.



Frequently Asked Questions

How do I install and set up react-offcanvas in a React project?

Run npm install react-offcanvas (or the yarn/pnpm equivalent)
in your project root. Then import the component and its stylesheet
at the top of the file where you want to use it:

import Offcanvas from 'react-offcanvas';
import 'react-offcanvas/dist/react-offcanvas.css';

Wrap your panel content in <Offcanvas isMenuOpened={yourBooleanState}>,
and control visibility by toggling that state with a button or any other trigger.
No global provider, no additional configuration — the component is ready to render.

How do I customize the position and style of a react-offcanvas component?

Use the position prop ("left", "right",
"top", "bottom") to control which edge the panel slides from.
Use the width prop (integer, pixels) to set the panel width for
left/right panels — and set this prop in sync with any CSS width override to keep
the push-effect math correct. For visual styling, pass a className prop
and write scoped CSS targeting .off-canvas-component__inner and
.off-canvas-overlay within that class scope.

What is the difference between react-offcanvas and building a custom side panel from scratch?

react-offcanvas handles the positioning logic, CSS transform direction,
push/overlay effect, and open/close state — work that typically takes
several hours to get right cross-browser. A custom implementation gives
more flexibility for non-standard behaviors like gesture-based dismissal,
multi-panel stacking, or complex animation choreography. For standard
navigation drawers, filter sidebars, and cart panels, react-offcanvas
is the faster and lower-risk choice. For highly customized or heavily animated
panels, consider Framer Motion or Headless UI instead.