AdminKitDocs
Components

React Offcanvas

A React offcanvas component on Bootstrap's markup: a side panel opened from state, four placements, backdrop and scroll options, a responsive sidebar mode.

An offcanvas is a panel that slides in from an edge of the window. Use it for content that should be close at hand without a page of its own, such as filters, a settings form or a long menu. The AdminKit sidebar uses it as the menu sheet on small screens.

Offcanvas renders Bootstrap's offcanvas from React state, without Bootstrap's JavaScript or react-bootstrap. It is in the free and PRO versions of AdminKit for React.

It works like the modal. The parent owns the open state: show opens the panel, and onHide asks to close it. While open, the panel locks the page scroll and keeps the focus inside. Unlike the modal, it stays in the DOM when it is hidden, as in Bootstrap.

Example

Inside Offcanvas, write a header and a body. They become the contents of .offcanvas.

import { useState } from "react";
import { CloseButton } from "@/components/bootstrap/close-button";
import { Offcanvas } from "@/components/bootstrap/offcanvas";

export function OrderFilters() {
  const [show, setShow] = useState(false);
  const close = () => setShow(false);

  return (
    <>
      <button
        className="btn btn-primary"
        type="button"
        aria-controls="offcanvasFilters"
        onClick={() => setShow(true)}
      >
        Filters
      </button>
      <Offcanvas
        id="offcanvasFilters"
        show={show}
        onHide={close}
        aria-labelledby="offcanvasFiltersLabel"
      >
        <div className="offcanvas-header">
          <h2 className="offcanvas-title h5" id="offcanvasFiltersLabel">Filters</h2>
          <CloseButton onClick={close} />
        </div>
        <div className="offcanvas-body">
          <p>Show only the orders that match.</p>
          <button type="button" className="btn btn-primary" onClick={close}>Apply</button>
        </div>
      </Offcanvas>
    </>
  );
}

onHide runs on Escape and on a click on the backdrop. Close the panel there by setting show to false.

Props

PropTypeDefaultDescription
showbooleanWhether the panel is open
onHide() => voidCalled on Escape and on a backdrop click; set show to false here
onShown() => voidCalled when the slide-in transition has ended
onHidden() => voidCalled when the slide-out transition has ended
placement"start" | "end" | "top" | "bottom""start"The edge it slides in from
responsive"sm" | "md" | "lg" | "xl" | "xxl"A sheet only below this breakpoint, part of the page from it up
backdropboolean | "static"truetrue: a backdrop that closes the panel on click; "static": a backdrop that does not; false: none
scrollbooleanfalseKeeps the page scrollable while the panel is open
keyboardbooleantrueEscape closes the panel
asElementType"div"The element, such as "nav" or "aside"

Other props, such as id, className, ref and aria-labelledby, go to the .offcanvas element. The component is in src/components/bootstrap/offcanvas.tsx.

Placement

Pick the edge that suits the content. placement adds .offcanvas-start (left), .offcanvas-end (right), .offcanvas-top or .offcanvas-bottom.

<Offcanvas
  id="offcanvasActivity"
  placement="end"
  show={show}
  onHide={close}
  aria-labelledby="offcanvasActivityLabel"
>
  <div className="offcanvas-header">
    <h2 className="offcanvas-title h5" id="offcanvasActivityLabel">Activity</h2>
    <CloseButton onClick={close} />
  </div>
  <div className="offcanvas-body">No new activity today.</div>
</Offcanvas>

Backdrop and scrolling

By default the panel has a backdrop and the page cannot scroll. Sometimes the user should keep working on the page while the panel is open. scroll keeps the page scrollable, and backdrop={false} removes the backdrop.

<Offcanvas
  id="offcanvasNotes"
  scroll
  backdrop={false}
  show={show}
  onHide={close}
  aria-labelledby="offcanvasNotesLabel"
>
  <div className="offcanvas-header">
    <h2 className="offcanvas-title h5" id="offcanvasNotesLabel">Notes</h2>
    <CloseButton onClick={close} />
  </div>
  <div className="offcanvas-body">Scroll the page: it stays scrollable while these notes are open.</div>
</Offcanvas>

backdrop="static" shows the backdrop but ignores clicks on it, so only Escape and your own buttons close the panel.

Responsive sidebar

Some panels should be a sheet on small screens and part of the page on large ones. With responsive, the element gets .offcanvas-lg (or another breakpoint) instead of .offcanvas. Below that breakpoint it is a sheet. From that breakpoint up, it is part of the page. The sidebar in src/components/sidebar/sidebar.tsx works this way, with as="nav" and responsive="lg". The navbar toggle opens it through the layout context.

<Offcanvas
  as="nav"
  id="sidebar"
  responsive="lg"
  show={sheetOpen}
  onHide={() => setSheetOpen(false)}
  aria-label="Sidebar"
>
  ...
</Offcanvas>

Accessibility

  • While open, the panel has role="dialog" and aria-modal="true". Point aria-labelledby at its title, or give it an aria-label.
  • Once it is visible, the focus moves to the panel, and Tab and Shift+Tab stay inside it. When it closes, the focus returns to the element that had it. As in Bootstrap, a panel with scroll and backdrop={false} does not trap the focus.
  • Escape closes the panel, unless keyboard={false}.
  • CloseButton has aria-label="Close". Set aria-controls on the button that opens the panel.

Last updated on

On this page