Offcanvas
Build hidden sidebars into your project for navigation, shopping carts, and more with the offcanvas component.
Interactive Playground
Controls
Event Log
- No events logged
Preview
Code
<script lang="ts">
let isShown = $state(false);
let trigger: HTMLElement | null = $state(null);
function toggleOffcanvas() {
isShown = !isShown;
}
</script>
<Button bind:elementRef={trigger} onclick={toggleOffcanvas}>
Toggle Offcanvas
</Button>
<Offcanvas.Root
placement="start"
isShown={isShown}
triggerElements={[trigger]}
onShow={handleShow}
onShown={handleShown}
onHide={handleHide}
onHidePrevented={handleHidePrevented}
onHidden={handleHidden}>
<Offcanvas.Header isDismissible={true}>
<Offcanvas.Title>Offcanvas Title</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>This is the offcanvas body content. You can place any content here, including forms, text, or other components.</p>
<div class="d-grid gap-2">
<Button colorVariant="secondary" onclick={() => isShown = false}>Close</Button>
</div>
</Offcanvas.Body>
</Offcanvas.Root>
Basic Example
The Offcanvas component is a sidebar that can be toggled to appear from any edge of the viewport. By default, it appears from the left side (start position).
<Button onclick={toggleOffcanvas}>
Launch Basic Offcanvas
</Button>
<Offcanvas.Root isShown={showOffcanvas} onHidden={() => showOffcanvas = false}>
<Offcanvas.Header>
<Offcanvas.Title>Offcanvas</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>Some text as placeholder. Content can be virtually anything.</p>
<Button colorVariant="secondary" onclick={() => showOffcanvas = false}>Close</Button>
</Offcanvas.Body>
</Offcanvas.Root>
Consumer-controlled triggers
Pass DOM references through triggerElements when a standalone button or link controls the panel. Use bind:this on a native element or bind:elementRef on Button. Your click handler owns visibility; an
enabled primary press on a listed element (including its descendants) is not treated as an outside dismissal.
HTML and SVG element references are supported. The list may contain multiple references and null or undefined entries; a null list means no triggers. Remove a reference to revoke ownership; detached elements are ignored and conditional bindings follow replacement elements. No extra listeners or global registrations are retained. Keep references reactive when changing the list. Each panel recognizes only its own list, and only the top overlay handles dismissal.
Native disabled controls (including disabled fieldsets) and elements with aria-disabled="true" do not receive this exemption. Non-primary
presses retain the existing outside-press behavior. A cancelled or abandoned primary press on an enabled owner leaves the panel unchanged; ownership
never schedules a later toggle. Without a backdrop, outside presses remain ignored.
Ownership does not add click handlers, set ARIA, or raise the trigger above a backdrop. Classes and aria-controls alone do
not establish ownership. This example uses z-index: 1042, between a single Offcanvas backdrop (1040) and panel (1045). The
panel covers the trigger where they overlap on narrow screens. Stacked overlays receive higher layers, so this fixed value is only
suitable for this single-overlay example; consumers control their trigger's stacking context. Keep consumer visibility in sync with
dismissals using onHide.
Prevented outside dismissals: 0
<script lang="ts">
import { Button, Offcanvas } from '@winkintel/bootstrap-svelte';
let open = $state(false);
let trigger: HTMLElement | null = $state(null);
</script>
<Button bind:elementRef={trigger} aria-controls="my-panel" aria-expanded={open}
style="position: relative; z-index: 1042;" onclick={() => open = !open}>
Toggle Offcanvas
</Button>
<Offcanvas.Root id="my-panel" isShown={open} triggerElements={[trigger]}
useBackdrop="static" placement="end" onHide={() => open = false}>
<Offcanvas.Body>Panel content</Offcanvas.Body>
</Offcanvas.Root>
Placement Options
Offcanvas supports four different placement options: start (default, left side), end (right side), top, and bottom.
<Button onclick={() => showPlacementStart = true}>
Offcanvas Start
</Button>
<Offcanvas.Root placement="start" isShown={showPlacementStart} onHidden={() => showPlacementStart = false}>
<Offcanvas.Header>
<Offcanvas.Title>Offcanvas Start</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>Positioned from the left of the screen (or right in RTL languages)</Offcanvas.Body>
</Offcanvas.Root>
<Button onclick={() => showPlacementEnd = true}>
Offcanvas End
</Button>
<Offcanvas.Root placement="end" isShown={showPlacementEnd} onHidden={() => showPlacementEnd = false}>
<Offcanvas.Header>
<Offcanvas.Title>Offcanvas End</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>Positioned from the right of the screen (or left in RTL languages)</Offcanvas.Body>
</Offcanvas.Root>
<Button onclick={() => showPlacementTop = true}>
Offcanvas Top
</Button>
<Offcanvas.Root placement="top" isShown={showPlacementTop} onHidden={() => showPlacementTop = false}>
<Offcanvas.Header>
<Offcanvas.Title>Offcanvas Top</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>Positioned from the top of the screen</Offcanvas.Body>
</Offcanvas.Root>
<Button onclick={() => showPlacementBottom = true}>
Offcanvas Bottom
</Button>
<Offcanvas.Root placement="bottom" isShown={showPlacementBottom} onHidden={() => showPlacementBottom = false}>
<Offcanvas.Header>
<Offcanvas.Title>Offcanvas Bottom</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>Positioned from the bottom of the screen</Offcanvas.Body>
</Offcanvas.Root>
Backdrop Options
You can control the behavior of the backdrop with the useBackdrop prop. It accepts three values: true (default, click outside to dismiss), "static" (backdrop present; outside clicks do not dismiss), or false (no backdrop). With false, outside clicks do not dismiss the panel. Use Escape (when keyboard dismissal is enabled), a
dismiss button, the navbar toggler, or the isShown prop to close it. If you need click-away dismissal, enable the backdrop or provide
your own outside-click handler.
When the panel is nested in a Navbar.Root, its Navbar.Toggler can open and close it with any backdrop setting,
provided the toggler is enabled and remains reachable above the backdrop. A controlling toggler click changes visibility once and does not
trigger onHidePrevented. Clicking outside a panel with a static backdrop still triggers that callback and keeps the panel open.
<Offcanvas.Root useBackdrop="static" isShown={showStaticBackdrop} onHidden={() => showStaticBackdrop = false}>
<Offcanvas.Header>
<Offcanvas.Title>Static Backdrop</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>Click the close button or press escape to close this offcanvas. Clicking outside won't close it.</p>
<Button colorVariant="secondary" onclick={() => showStaticBackdrop = false}>Close</Button>
</Offcanvas.Body>
</Offcanvas.Root>
<Offcanvas.Root useBackdrop={true} isShown={showTrueBackdrop} onHidden={() => showTrueBackdrop = false}>
<Offcanvas.Header>
<Offcanvas.Title>True Backdrop</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>This offcanvas has a backdrop that you can click outside to close it.</p>
</Offcanvas.Body>
</Offcanvas.Root>
<Offcanvas.Root useBackdrop={false} isShown={showFalseBackdrop} onHidden={() => showFalseBackdrop = false}>
<Offcanvas.Header>
<Offcanvas.Title>False Backdrop</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>There is no backdrop for this offcanvas. You can still interact with the page content.</p>
<Button colorVariant="secondary" onclick={() => showFalseBackdrop = false}>Close</Button>
</Offcanvas.Body>
</Offcanvas.Root>
Scrollable Body
By default, the Offcanvas component prevents scrolling of the main body when it's open. You can allow body scrolling by setting the isBodyScrollable prop to true.
<Offcanvas.Root isBodyScrollable={true} isShown={showScrollable} onHidden={() => showScrollable = false}>
<Offcanvas.Header>
<Offcanvas.Title>Scrollable Offcanvas</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>The page body remains scrollable when this offcanvas is open.</p>
<p>Try scrolling the page while this offcanvas is open.</p>
<Button colorVariant="secondary" onclick={() => showScrollable = false}>Close</Button>
</Offcanvas.Body>
</Offcanvas.Root>
Responsive Behaviors
The Offcanvas component can be configured to behave responsively using the showOnBreakpoint prop. When set, the component will
be visible as a regular element at or above the specified breakpoint, and will behave as an offcanvas below it.
Supported breakpoints are sm through xxl. Omit showOnBreakpoint for a standalone dismissible
overlay, or to inherit a parent Navbar's mode. For always-inline Navbar content, use Navbar.Root expandOnBreakpoint="xs" and
omit the panel's breakpoint. xs is not an Offcanvas breakpoint: remove legacy showOnBreakpoint="xs" usages.
Untyped runtime values of xs are treated as omitted, preserving Navbar inheritance without generating an invalid CSS class.
This example will show as a regular sidebar on screens lg and up, but as an offcanvas on smaller screens.
<Button colorVariant="primary" class="d-lg-none" onclick={() => showResponsive = !showResponsive}>
Toggle Responsive Offcanvas
</Button>
<Offcanvas.Root showOnBreakpoint="lg" placement="start">
<Offcanvas.Header>
<Offcanvas.Title>Responsive Offcanvas</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<p>This is always visible on lg screens and above, but behaves like a regular offcanvas below lg breakpoint.</p>
<Button colorVariant="secondary" class="d-lg-none" onclick={() => showResponsive = false}>Close</Button>
</Offcanvas.Body>
</Offcanvas.Root>
API Reference
Offcanvas.Root Props
| Name | Type | Default | Description |
|---|---|---|---|
class | string | - | Additional CSS classes to apply to the component |
dir | 'ltr' | 'rtl' | 'ltr' | Text direction, affects the side from which the offcanvas appears |
elementRef | HTMLElement | null | null | Reference to the DOM element |
id | string | Auto-generated | Unique identifier for the offcanvas |
isBodyScrollable | boolean | false | When true, allows scrolling the main page body when offcanvas is open |
isKeyboardDismissible | boolean | true | When true, allows closing the offcanvas with the Escape key |
isShown | boolean | false | Controls the visibility of the offcanvas |
placement | 'start' | 'end' | 'top' | 'bottom' | 'start' | Determines the edge of the screen from which the offcanvas appears |
showOnBreakpoint | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | undefined | Inline at or above the selected breakpoint. Omitted inherits a parent Navbar, or keeps a standalone panel as an overlay.
Legacy runtime xs is treated as omitted. |
triggerElements | readonly (Element | null | undefined)[] | null | [] | DOM references to consumer-controlled triggers. Enabled primary presses leave visibility to the consumer click handler. |
useBackdrop | 'static' | boolean | true | Controls backdrop behavior: true (default, clickable), "static" (not clickable), false (no backdrop or outside-click dismissal) |
onHide | EventListener | - | Callback when the offcanvas begins hiding |
onHidePrevented | EventListener | - | Callback when the offcanvas is shown, its backdrop is static and a click outside of the offcanvas is performed. The event is also fired when the escape key is pressed and the keyboard option is set to false. |
onHidden | EventListener | - | Callback when the offcanvas has finished hiding |
onShow | EventListener | - | Callback when the offcanvas begins showing |
onShown | EventListener | - | Callback when the offcanvas has finished showing |
Offcanvas.Header Props
| Name | Type | Default | Description |
|---|---|---|---|
class | string | - | Additional CSS classes to apply to the component |
elementRef | HTMLElement | null | null | Reference to the DOM element |
isDismissible | boolean | false | When true, displays a close button in the header |
Offcanvas.Title Props
| Name | Type | Default | Description |
|---|---|---|---|
class | string | - | Additional CSS classes to apply to the component |
elementRef | HTMLElement | null | null | Reference to the DOM element |
level | 1 | 2 | 3 | 4 | 5 | 6 | 5 | Heading level to use for the title (h1-h6) |
Offcanvas.Body Props
| Name | Type | Default | Description |
|---|---|---|---|
class | string | - | Additional CSS classes to apply to the component |
elementRef | HTMLElement | null | null | Reference to the DOM element |
CSS Classes
The component applies Bootstrap's offcanvas classes based on the provided props:
offcanvas- Base class for the Offcanvas componentoffcanvas-[breakpoint]- Applied forsmthroughxxl; there is nooffcanvas-xsclassoffcanvas-[placement]- Position classes (start, end, top, bottom)show- Applied when the offcanvas is visibleoffcanvas-header- Applied to Offcanvas.Header componentsoffcanvas-title- Applied to Offcanvas.Title componentsoffcanvas-body- Applied to Offcanvas.Body componentsoffcanvas-backdrop- Applied to the backdrop when enabled
Responsive Behavior
The Offcanvas component can be configured for responsive behavior:
- When
showOnBreakpointis set, the component appears as a regular element on screens at or above the specified breakpoint - On screens below the breakpoint, it behaves as a regular offcanvas that must be toggled to appear
- This enables creating responsive sidebar-like layouts that collapse into an offcanvas on smaller screens