Offcanvas
A panel that slides in from any edge of the viewport for navigation, filters,
carts, or secondary content. Composed from OffcanvasHeader,
OffcanvasTitle, and OffcanvasBody.
Import
import {
Offcanvas,
OffcanvasHeader,
OffcanvasTitle,
OffcanvasBody,
} from '@metatoy/bootstrap-styled'; Launch an offcanvas
Like Modal, Offcanvas is controlled — own the show state and pass
onHide.
The full controlled pattern:
import { useState } from 'react';
import { Offcanvas, OffcanvasHeader, OffcanvasTitle, OffcanvasBody, Button, CloseButton } from '@metatoy/bootstrap-styled';
function Example() {
const [show, setShow] = useState(false);
return (
<>
<Button variant="secondary" onClick={() => setShow(true)}>Open offcanvas</Button>
<Offcanvas show={show} onHide={() => setShow(false)} placement="end">
<OffcanvasHeader>
<OffcanvasTitle>Offcanvas</OffcanvasTitle>
<CloseButton onClick={() => setShow(false)} />
</OffcanvasHeader>
<OffcanvasBody>Panel content.</OffcanvasBody>
</Offcanvas>
</>
);
} Overlay scope. The open panel renders through a portal, fixed to the viewport edge, with a backdrop over the whole page — that is the real component behavior, not something scoped to this demo card. Escape or a backdrop click dismisses it.
Panel surface
The Offcanvas* parts are plain styled elements, so you can render the panel
surface inline (without the slide-in overlay) to preview its structure:
Placements
placement="start" | "end" | "top" | "bottom" chooses the edge the panel
enters from. start/end are 400px-wide side panels; top/bottom are
30vh-tall drawers.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| show● | boolean | — | — |
| onHide● | () => void | — | — |
| placement | "start" | "end" | "top" | "bottom" | — | — |
| children | React.ReactNode | — | — |
The subcomponents — OffcanvasHeader, OffcanvasTitle, OffcanvasBody — are
styled elements that accept all native attributes for their tag.
Theming
The panel reads --bs-body-bg / --bs-body-color for its fill and
--bs-border-color-translucent for the edge border; the header divider uses
--bs-border-color. No box-shadow is applied by default (matching Bootstrap).
Because the panel is portaled, the library re-applies the active theme’s
variables inside the portal so your token overrides still reach it.
Accessibility
The panel is rendered with aria-modal="true" and a dialog role, and traps
focus while open (restoring it to the trigger on close). Provide an
OffcanvasTitle and wire a CloseButton to onHide. Escape closes the panel.