Offcanvas
Build hidden sidebars into your project. Sidebars can aid in enhancing user interaction or preventing further interaction.
<template>
<BButton @click="click">Show OffCanvas</BButton>
<BOffcanvas v-model="show" />
</template>
<script setup lang="ts">
import {ref} from 'vue'
const show = ref(false)
const click = () => {
show.value = !show.value
}
</script>
Customizing Location
Customize location with four standard options top, bottom, start, end
.
<template>
<BButton class="m-2" @click="click('start')">Show start</BButton>
<BButton class="m-2" @click="click('end')">Show end</BButton>
<BButton class="m-2" @click="click('bottom')">Show bottom</BButton>
<BButton class="m-2" @click="click('top')">Show top</BButton>
<BOffcanvas v-model="show" :placement="placement" />
</template>
<script setup lang="ts">
import {ref} from 'vue'
import {type Placement} from 'bootstrap-vue-next'
const show = ref(false)
const placement = ref<Placement>('start')
const click = (place: Placement = 'start') => {
placement.value = place
show.value = !show.value
}
</script>
Responsive
The responsive
prop in the BOffcanvas component enables adaptive display behavior based on the viewport size. When set, the offcanvas content displays inline for viewports wider than the specified breakpoint, and as a traditional offcanvas for narrower viewports
NOTE
In SSR environments, the BOffcanvas component must be rendered client-side due to its dependency on browser context for responsive behavior. Use client-only directives or components to ensure proper functionality
<BOffcanvas responsive="md" />
Sidebar
One common use of offcanvas is to create a table of contents sider that is visible on a large screen, but becomes an offcanvas component below a certain breakpoint. This is common for documentation sites like the one you're reading now which includes two such sidebars - a "table of contents" on the left and a "on this page" on the right.
Below is a simple example showing how to set up such a site.
<template>
<BContainer fluid="xxl">
<BRow class="d-md-none"
><BCol>
<BButton variant="link" underline-opacity="0" @click="showToc"
>< Table of Contents</BButton
> </BCol
><BCol
><div class="text-end">
<BButton variant="link" underline-opacity="0" @click="showOtp">On this page ></BButton>
</div></BCol
></BRow
>
<BRow>
<BCol md="2" class="scrollable-column">
<BOffcanvas v-model="tocVisible" placement="start" responsive="md">
<ul>
<ul>
<li v-for="item in toc" :key="item">{{ item }}</li>
<li>A very large entry in table of contents</li>
</ul>
</ul>
</BOffcanvas>
</BCol>
<BCol md="8">
Lorem ipsum odor amet, consectetuer adipiscing elit. Pellentesque imperdiet libero litora
nisl condimentum ullamcorper. Convallis vel auctor morbi mauris lectus mi. Consequat urna
tempus tristique id augue luctus. Quis ullamcorper faucibus laoreet dapibus elit. Habitant
vulputate maecenas varius massa dui dolor augue. Dictum aliquam justo tempor integer
placerat scelerisque per duis facilisis. Orci ex parturient fusce vestibulum quis euismod
elementum habitasse. Vitae suscipit vehicula pellentesque massa facilisi erat dapibus. Urna
torquent praesent eleifend lobortis nec ullamcorper dignissim eu? Neque scelerisque leo nisl
duis taciti faucibus nam ut. Sit ultrices aenean magna nam cubilia lacus torquent? Eu purus
posuere ad metus ultrices. Netus id hac lobortis vel habitant ad morbi finibus vulputate.
Luctus pellentesque metus diam curae nostra praesent ullamcorper elit. Varius commodo
scelerisque in libero magna senectus. Magnis dis metus penatibus finibus mattis velit. Leo
imperdiet metus accumsan nascetur mus id. Torquent elementum natoque dictum risus non mollis
euismod mauris mus. Lobortis aliquet class at scelerisque egestas at scelerisque condimentum
tellus. Mollis consequat dui risus consequat ipsum, penatibus neque. Ultrices tristique sed
parturient ex integer ac tristique fusce. Amet imperdiet condimentum enim semper proin
lacinia. Montes in iaculis dui penatibus vulputate nulla; mauris venenatis. Malesuada metus
luctus dictumst; ante sapien dis facilisi himenaeos. Finibus nibh ut est faucibus ut
molestie. Magna nascetur imperdiet imperdiet; fringilla velit ac condimentum ullamcorper.
Ornare arcu feugiat phasellus nisl morbi porta. Auctor praesent duis quam euismod maximus et
varius. Parturient class proin tristique egestas consequat. Eleifend magna dictum aenean,
sagittis diam gravida. Taciti nam donec fringilla commodo ullamcorper accumsan fames ante
accumsan. Quis nulla porttitor blandit interdum vel scelerisque senectus. Odio lorem blandit
aliquam tempor, eu egestas maecenas ligula. Commodo egestas maximus euismod eleifend natoque
sagittis; viverra himenaeos. Vel dis justo odio conubia fringilla bibendum scelerisque dis.
Lorem pulvinar dapibus porta viverra massa potenti laoreet fringilla. Augue odio gravida a
nunc rhoncus aliquet parturient imperdiet natoque. Hac neque semper pretium, parturient hac
nascetur. Elit mollis convallis sagittis scelerisque tristique. Fermentum justo efficitur
congue nascetur mi eu amet conubia. Purus semper blandit feugiat hac vel sapien convallis
quisque. Mi justo tempus quis enim tellus? Risus et ipsum in; aliquam nisl habitant.
Praesent laoreet luctus nunc; pellentesque aptent porta. Vulputate risus lacinia imperdiet
aptent habitasse facilisi venenatis ipsum. Integer varius porttitor aenean laoreet maecenas
luctus id viverra porta. Habitasse lorem bibendum semper maecenas volutpat porta. Eros
praesent duis odio sagittis metus montes interdum.
</BCol>
<BCol md="2" class="scrollable-column">
<BOffcanvas v-model="otpVisible" placement="end" responsive="md">
<ul>
<li v-for="item in otp" :key="item">{{ item }}</li>
<li>A very large entry in on this page</li>
</ul>
</BOffcanvas>
</BCol>
</BRow>
</BContainer>
</template>
<script setup lang="ts">
import {ref} from 'vue'
const otpVisible = ref(false)
const tocVisible = ref(false)
// This example uses buttons to show the hidden sidebars, but you could also use a menu item or any other
// UX element that can change the state of the model value for the BOffcanvas
const showOtp = () => {
otpVisible.value = true
}
const showToc = () => {
tocVisible.value = true
}
const tocGenerator = (s: string, c: number): string[] =>
[...Array(c).keys()].map((i) => `${s} ${i}`)
const toc = tocGenerator('Chapter', 100) // Replace this with your own table of contents
const otp = tocGenerator('Section', 100) // Replace this with your own "on this page"
</script>
<style scoped lang="scss">
// The styling below makes a colum scroll independently of the rest of the page if
// its content is too large to fit in the current viewport
.scrollable-column {
max-height: 100vh;
overflow-y: auto;
position: sticky;
top: 0;
}
</style>
Component Reference
<BOffcanvas>
Prop | Type | Default | Description |
---|---|---|---|
backdrop-first | boolean | false | Animate the backdrop before the offcanvas, and on leave animate the offcanvas before the backdrop |
body-attrs | Readonly<AttrsValue> | undefined | |
body-class | ClassValue | undefined | |
body-scrolling | boolean | false | |
footer-class | string | undefined | |
header-class | string | undefined | |
header-close-class | ClassValue | undefined | |
header-close-label | string | 'Close' | |
header-close-variant | ButtonVariant | null | 'secondary' | |
id | string | undefined | |
initial-animation | boolean | false | When set, enables the initial animation on mount |
lazy | boolean | false | When set, the content will not be mounted until opened |
model-value | boolean | false | Controls the visibility of the component |
no-animation | boolean | false | When set, disables the animation |
no-backdrop | boolean | false | |
no-close-on-backdrop | boolean | false | |
no-close-on-esc | boolean | false | |
no-fade | boolean | false | Alias for `noAnimation` |
no-focus | boolean | false | |
no-header | boolean | false | |
no-header-close | boolean | false | |
no-trap | boolean | false | Disables the focus trap feature |
placement | Placement | 'start' | |
responsive | Breakpoint | undefined | |
shadow | Size | boolean | false | |
show | boolean | false | When set, and prop 'visible' is false on mount, will animate from closed to open on initial mount. Mainly to help with template show. Use model-value for reactive show/hide |
teleport-disabled | boolean | false | |
teleport-to | string | RendererElement | null | undefined | 'body' | |
title | string | undefined | |
trans-props | TransitionProps | undefined | Transition properties |
unmount-lazy | boolean | false | When set and `lazy` is true, the content will be unmounted when closed |
visible | boolean | false | When 'true', open without animation |
width | string | undefined |
Event | Args | Description |
---|---|---|
breakpoint | value : BvTriggerableEvent - The eventopened : boolean - Whether or not the offcanvas is above the breakpoint and is open by it | Emitted when the offcanvas' breakpoint state changes |
close | value : BvTriggerableEvent | |
esc | value : BvTriggerableEvent | |
hidden | value : BvTriggerableEvent | |
hide | value : BvTriggerableEvent | |
hide-prevented | ||
show | value : BvTriggerableEvent | |
show-prevented | ||
shown | value : BvTriggerableEvent | |
update:model-value | update:model-value : boolean |
Name | Scope | Description |
---|---|---|
backdrop | ||
default | visible : boolean placement : 'top' | 'bottom' | 'start' | 'end' hide : (trigger?: string) => void | |
footer | visible : boolean placement : 'top' | 'bottom' | 'start' | 'end' hide : (trigger?: string) => void | |
header | visible : boolean placement : 'top' | 'bottom' | 'start' | 'end' hide : (trigger?: string) => void | |
header-close | ||
title | visible : boolean placement : 'top' | 'bottom' | 'start' | 'end' hide : (trigger?: string) => void |