Skip to component content

A side panel that slides from the viewport edge using Bootstrap's Offcanvas behavior.

Filter results

Narrow the list by status, owner, and date range before applying a bulk action.

Usage

Use .offcanvas for secondary controls or details that should slide in from a viewport edge without navigating away from the current page. Connect a clear trigger to the offcanvas id, choose the placement edge, and label the panel from its visible header.

Keep the sheet body focused on one supporting task, such as filters, settings, or contextual details. Configure backdrop and body scrolling intentionally: a backdrop keeps attention inside the sheet, while body scrolling can preserve page context for lighter side panels.

Sheet uses Bootstrap Offcanvas behavior, so Bootstrap owns focus management, Escape dismissal, backdrop clicks, and reduced-motion-aware transitions. Keep essential page actions outside the sheet unless the sheet is already open, and avoid placing another overlay workflow inside it.

For a URL that should display an open sheet on load, add data-sheet-open-on-load="true" to the .offcanvas.sheet element and call the public initSheets(root) ESM initializer after Bootstrap is available. The initializer consumes the one-time marker; ordinary sheets remain closed until their trigger is used. Keep the returned cleanup function and call it when releasing that root to remove Sheet autofocus listeners.

Set --bs-offcanvas-width on the panel to customize its horizontal width, for example --bs-offcanvas-width: 30rem for a wider sheet. Omit the override to inherit the theme's default width. Bootstrap limits the panel to the available screen width.

Basic

Use sheet_target on a button to open an off-canvas panel while staying in your current context.

Sheet
<div class="d-inline-flex">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-basic">Open sheet</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-basic"
      aria-labelledby="sheet-basic-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-basic-title">Filter results</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">Narrow the list below by status, owner, and date range before a bulk action.</p>
  </div>
</div>

Filter results

Narrow the list below by status, owner, and date range before a bulk action.

Placement

Control which screen edge anchors the panel by setting placement to start, end, top, or bottom.

Sheet
<div class="d-inline-flex flex-wrap gap-2">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-start">Start</button>
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-end">End</button>
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-top">Top</button>
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-bottom">Bottom</button>
</div>
<div
    class="offcanvas offcanvas-start sheet"
    tabindex="-1"
    id="sheet-start"
      aria-labelledby="sheet-start-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-start-title">From the start edge</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">
      Set <code>placement="start"</code>.
    </p>
  </div>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-end"
      aria-labelledby="sheet-end-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-end-title">From the end edge</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">
      Set <code>placement="end"</code>, the default.
    </p>
  </div>
</div>
<div
    class="offcanvas offcanvas-top sheet"
    tabindex="-1"
    id="sheet-top"
      aria-labelledby="sheet-top-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-top-title">From the top edge</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">
      Set <code>placement="top"</code>.
    </p>
  </div>
</div>
<div
    class="offcanvas offcanvas-bottom sheet"
    tabindex="-1"
    id="sheet-bottom"
      aria-labelledby="sheet-bottom-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-bottom-title">From the bottom edge</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">
      Set <code>placement="bottom"</code>.
    </p>
  </div>
</div>

From the start edge

Set placement="start".

From the end edge

Set placement="end", the default.

From the top edge

Set placement="top".

From the bottom edge

Set placement="bottom".

Body scrolling

Prefer backdrop=false + scroll=true only when users need to keep the page context visible.

Sheet
<div class="d-inline-flex">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-scroll">Open with body scrolling</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-scroll"
      aria-labelledby="sheet-scroll-title"
data-bs-backdrop="false"data-bs-scroll="true"  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-scroll-title">Body scrolling</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">
      Use <code>backdrop=false</code> + <code>scroll=true</code> when users need to keep the page context visible.
    </p>
  </div>
</div>

Body scrolling

Use backdrop=false + scroll=true when users need to keep the page context visible.

No close button

Hide the header close affordance with dismiss=false and provide an explicit in-panel action instead.

Sheet
<div class="d-inline-flex">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-no-close">Open without a close button</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-no-close"
      aria-labelledby="sheet-no-close-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-no-close-title">Confirm export</h2>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="text-body-secondary mb-3">Close from in-panel actions when a quick decision is required.</p>
    <div class="d-inline-flex gap-2">
      <button class="btn btn-outline-secondary" type="button" data-bs-dismiss="offcanvas">Cancel</button>
      <button class="btn btn-primary" type="button" data-bs-dismiss="offcanvas">Export</button>
    </div>
  </div>
</div>

Confirm export

Close from in-panel actions when a quick decision is required.

Task workflow panel

Use a panel as a compact context surface for related operations that should stay connected to the main page.

Sheet
<div class="d-inline-flex">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-workflow">Open task panel</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-workflow"
      aria-labelledby="sheet-workflow-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-workflow-title">Task workflow</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <div class="vstack gap-3">
      <p class="text-body-secondary mb-0">Review assignment, due date, and reviewer notes before publishing.</p>
      <button class="btn btn-outline-secondary" type="button">Assign reviewer</button>
      <button class="btn btn-primary" type="button">Move to queue</button>
    </div>
  </div>
</div>

Task workflow

Review assignment, due date, and reviewer notes before publishing.

RTL

Switch the sheet content direction for Arabic, Hebrew, and English workflows with the same placement and content shape.

<div dir="rtl">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-rtl">فتح لوحة التصفية</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-rtl"
      aria-labelledby="sheet-rtl-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-rtl-title">تصفية العناصر</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">قم بتصفية القائمة حسب الحالة والمالك ونطاق التاريخ.</p>
  </div>
</div>

تصفية العناصر

قم بتصفية القائمة حسب الحالة والمالك ونطاق التاريخ.

<div dir="rtl">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-rtl-hebrew">פתח חלון סינון</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-rtl-hebrew"
      aria-labelledby="sheet-rtl-hebrew-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-rtl-hebrew-title">סינון פריטים</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">סנן את הרשימה לפי סטטוס, בעלים ורצף תאריכים.</p>
  </div>
</div>

סינון פריטים

סנן את הרשימה לפי סטטוס, בעלים ורצף תאריכים.

<div dir="ltr">
  <button class="btn btn-outline-secondary" type="button" data-bs-toggle="offcanvas" data-bs-target="#sheet-rtl-english">Open filter panel</button>
</div>
<div
    class="offcanvas offcanvas-end sheet"
    tabindex="-1"
    id="sheet-rtl-english"
      aria-labelledby="sheet-rtl-english-title"
  >
  <div class="offcanvas-header">
    <h2 class="offcanvas-title" id="sheet-rtl-english-title">Filter items</h2>
    <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="offcanvas"></button>
  </div>
  <div class="offcanvas-body scroll-fade-y no-scrollbar">
    <p class="mb-0">Filter the list below by status, owner, and date range.</p>
  </div>
</div>

Filter items

Filter the list below by status, owner, and date range.

Component reference

Status
Accepted
Runtime
optional Moo UI ESM
Markup
Moo UI documented extension
Reference

For Bootstrap's Offcanvas plugin options, focus management, and accessibility notes, see the Bootstrap Offcanvas documentation.

Settings

Theme
Choose how Moo UI looks on this device.
Sidebar
Choose how the sidebar sits on the page.
Theme Builder
Tune catalog tokens and chart colors for this browser.
Base color
Theme color
Chart color
Heading font
Body font
Radius
Direction
Choose the reading direction. RTL flips the layout live.
Reset

Restore the default appearance.