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
| Prop | Type | Default | Description |
|---|---|---|---|
show | boolean | Whether the panel is open | |
onHide | () => void | Called on Escape and on a backdrop click; set show to false here | |
onShown | () => void | Called when the slide-in transition has ended | |
onHidden | () => void | Called 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 | |
backdrop | boolean | "static" | true | true: a backdrop that closes the panel on click; "static": a backdrop that does not; false: none |
scroll | boolean | false | Keeps the page scrollable while the panel is open |
keyboard | boolean | true | Escape closes the panel |
as | ElementType | "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"andaria-modal="true". Pointaria-labelledbyat its title, or give it anaria-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
scrollandbackdrop={false}does not trap the focus. - Escape closes the panel, unless
keyboard={false}. CloseButtonhasaria-label="Close". Setaria-controlson the button that opens the panel.
Related
- Next.js offcanvas: the same component in AdminKit for Next.js
- Bootstrap 5 offcanvas: the offcanvas of the HTML version
- Modal: a dialog in the middle of the window
- Offcanvas demo: panels from every edge, with and without a backdrop
Last updated on
Modal
A React Bootstrap modal component: open the dialog from state, close it on Escape or a backdrop click. Static, centered, scrollable and fullscreen variants.
PlaceholdersPro
React Bootstrap placeholder markup in AdminKit PRO: a loading card with the same layout as the real one, widths, sizes, colors and glow or wave animation.