Offcanvas
Sliding side panel component based on Bootstrap 5, with optional extension panel support.
Description
Offcanvas is a wrapper around Bootstrap 5’s Offcanvas component for showing side panels (by default from the right edge). It provides slots for title, body, header, and footer, and optionally an extension panel that can be shown or hidden for detail or list/detail views.
Location: @/components/interface/Bootstrap/Offcanvas.vue
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
extension | Boolean | ❌ No | false | Enables the extension panel (half-screen) |
fotter | Boolean | ❌ No | true | Shows the footer area (note: prop name has typo "fotter") |
extensionFotter | Boolean | ❌ No | true | Shows footer in the extension panel when active |
extended | Boolean | ❌ No | false | Initial state of the extension panel (open/closed) |
align | String | ❌ No | 'end' | Side from which the panel opens: 'start' or 'end' |
keyboard | Boolean | ❌ No | true | Allows closing with Escape key (via data-bs-keyboard) |
class | String | ❌ No | '' | Additional CSS classes for the offcanvas |
menuClass | String | ❌ No | '' | Additional CSS classes for the menu |
options | Object | ❌ No | {} | Options passed to the Bootstrap Offcanvas constructor |
onClose | Function | ❌ No | () => {} | Callback when the offcanvas closes |
closeOnEscape | Boolean | ❌ No | true | Closes the offcanvas when Escape is pressed (manual listener) |
Slots
title
Main panel title.
<template #title>
{{ t('offcanvas.details') }}
</template>subtitle
Subtitle below the title.
<template #subtitle>
<span class="badge bg-primary">ID: {{ id }}</span>
</template>header
Extra content in the header (to the right of the title, before the close button).
<template #header>
<button class="btn btn-sm btn-outline-primary">Action</button>
</template>body
Main panel content.
<template #body>
<div class="details">...</div>
</template>fotter
Footer actions (slot name has typo "fotter" as in the component).
<template #fotter>
<button class="btn btn-primary" @click="save">{{ t('actions.save') }}</button>
</template>Extension panel slots (when extension is true)
extension-title: Extension panel title.extension-header: Extension panel header content.extension: Extension panel body (e.g. list).extension-footer: Extension panel footer.
Exposed Methods
The component exposes the following methods via ref:
show()
Opens the offcanvas.
const offcanvas = ref(null);
offcanvas.value.show();hide()
Closes the offcanvas.
offcanvas.value.hide();extension.show()
Opens the extension panel (when extension is true).
offcanvas.value.extension.show();extension.hide()
Closes the extension panel.
offcanvas.value.extension.hide();el
Reference to the Bootstrap Offcanvas instance (for advanced use).
Events
| Event | Payload | Description |
|---|---|---|
update:extended | boolean | Emitted when the extension panel state changes |
close | event | Emitted when the offcanvas closes (hidden.bs.offcanvas) |
Extension panel behavior
When extension is true:
- The offcanvas can be shown in two widths: normal (
w-33) or extended (w-66), except on mobile where it is alwaysw-100. extension.show()andextension.hide()switch between normal view and the view with the extension panel visible.- The extension panel has its own title, body, and footer via the
extension-title,extension,extension-footerslots, etc. - The
extendedprop andupdate:extendedevent allow binding the state with the parent (v-model:extended).
Usage Examples
Basic Example
<template>
<button class="btn btn-primary" @click="offcanvas.show()">Open</button>
<Offcanvas ref="offcanvas">
<template #title>{{ t('offcanvas.info') }}</template>
<template #body>
<p>Panel content.</p>
</template>
<template #fotter>
<button class="btn btn-primary" @click="offcanvas.hide()">
{{ t('actions.close') }}
</button>
</template>
</Offcanvas>
</template>
<script setup>
import { ref } from 'vue';
import Offcanvas from '@/components/interface/Bootstrap/Offcanvas.vue';
const offcanvas = ref(null);
</script>Example with Extension Panel
<template>
<Offcanvas
ref="offcanvas"
:extension="true"
v-model:extended="isExtended"
>
<template #title>Detail</template>
<template #body>
<div>Main content (e.g. item detail).</div>
<button class="btn btn-link" @click="offcanvas.extension.show()">
View list
</button>
</template>
<template #extension-title>List</template>
<template #extension>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<template #fotter>
<button class="btn btn-primary">Save</button>
</template>
</Offcanvas>
</template>
<script setup>
import { ref } from 'vue';
import Offcanvas from '@/components/interface/Bootstrap/Offcanvas.vue';
const offcanvas = ref(null);
const isExtended = ref(false);
const items = ref([]);
</script>Important Notes
Footer slot name: The footer actions slot is named
fotter(intentional typo in the component). Use<template #fotter>.Widths: On desktop, without extension it uses
w-33, with extensionw-66. On mobile it is alwaysw-100.Close on Escape: If
closeOnEscapeis true, a globalkeydownlistener is added to close on Escape; the offcanvas also closes via Bootstrap’s native behavior whenkeyboardis true.Bootstrap: Depends on Bootstrap 5 (
bs.Offcanvas). It must be loaded in the project.Responsive: The component uses the
useBreakPoints()composable to detectisMobileand adjust width.
