useToast
The useToast composable allows you to create and manage toasts programmatically from anywhere in your application. It provides a simple API to show toast messages without needing to declare toast components in your templates.
Setup
To use useToast, you need one of the following setup approaches:
BApp Component (Recommended)
The easiest way is to wrap your application with the BApp component, which automatically sets up the orchestrator and registry:
<template>
<BApp>
<RouterView />
</BApp>
</template> Due to how Vue's provide/inject system works, composables like useToast(), useModal(), and usePopover()cannot be called in the same component that declares <BApp>. They rely on values provided by BApp, and Vue's inject only works in child components — not in the component that calls provide itself.
Place <BApp> at least one component level above where these composables are called.
Plugin Setup (Legacy)
Alternatively, you can use the traditional plugin approach.
<BApp>, you must have initialized the createBootstrap plugin for this to work properly. Read hereBasic Usage
Creating and showing a toast is simple:
<template>
<BButton @click="showToast">Show</BButton>
</template>
<script setup lang="ts">
import { BButton } from 'bootstrap-vue-next/components/BButton'
import { useToast } from 'bootstrap-vue-next/composables/useToast'
const { create } = useToast()
const showToast = async () => {
await using _ = await create({ title: 'Hello', body: 'World' }).show()
}
</script>The create method returns a controller object. Use .show() to display the toast and await the returned promise if you need to react to the close event.
Create Options
The create method accepts an object with BToast’s props, position, appendToast, component, and slots.
The position value affects placement; its type is ContainerPosition.
Lifecycle options are passed in options on the create payload. resolveOnHide resolves the promise when hide starts instead of after the full hide lifecycle.
Reactivity Within create
create props property can accept a MaybeRef, meaning that you can make properties reactive
<template>
<BButton @click="showMe">Show</BButton>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import { type ColorVariant, type ToastOrchestratorCreateParamBase } from 'bootstrap-vue-next'
import { BButton } from 'bootstrap-vue-next/components/BButton'
import { useToast } from 'bootstrap-vue-next/composables/useToast'
const { create } = useToast()
const body = ref('foo')
let intervalId: ReturnType<typeof setInterval> | undefined
onMounted(() => {
intervalId = setInterval(() => {
body.value = body.value === 'foo' ? 'bar' : 'foo'
}, 1000)
})
onUnmounted(() => {
if (intervalId !== undefined) {
clearInterval(intervalId)
}
})
// `create()` needs a writable ref/plain object (it needs to control its own state),
// so `reactive()` is not used here. Instead, derive the reactive pieces with `computed()`,
// then sync them onto a plain ref via `watchEffect()`.
const derivedVariant = computed(() => (body.value === 'foo' ? 'danger' : 'info') as ColorVariant)
const myToast = ref<ToastOrchestratorCreateParamBase>({
body: body.value,
variant: derivedVariant.value,
})
watchEffect(() => {
myToast.value.body = body.value
myToast.value.variant = derivedVariant.value
})
const showMe = async () => {
await using _ = await create(myToast).show()
}
</script>Advanced usage
Using props can work for most situations, but it leaves some finer control to be desired. For instance, you can add HTML to any slot value. This can either be an imported SFC or an inline render function. For reactivity, you must use a getter function.
<template>
<BButton @click="showMe">Show</BButton>
</template>
<script setup lang="ts">
import { h, markRaw, onMounted, onUnmounted, ref } from 'vue'
import type { OrchestratedToast } from 'bootstrap-vue-next'
import { BButton } from 'bootstrap-vue-next/components/BButton'
import { useToast } from 'bootstrap-vue-next/composables/useToast'
const { create } = useToast()
const firstRef = ref<OrchestratedToast>({
body: 'foo',
})
let intervalId: ReturnType<typeof setInterval> | undefined
onMounted(() => {
intervalId = setInterval(() => {
firstRef.value.body = firstRef.value.body === 'foo' ? 'bar' : 'foo'
}, 1000)
})
onUnmounted(() => {
if (intervalId !== undefined) {
clearInterval(intervalId)
}
})
const showMe = async () => {
await using _ = await create({
body: firstRef.value.body,
slots: { default: () => markRaw(h('div', null, `custom! ${firstRef.value.body}`)) },
}).show()
// Demonstration pseudocode, you can also import a component and use it
// const importedComponent = () => {
// create({
// component: markRaw((await import('./MyToastComponent.vue')).default),
// })
// }
}
</script>Programmatically Hiding a Toast
Hiding a Toast programmatically is simple. The controller returned by create exposes methods like show, hide, and destroy.
<template>
<BButtonGroup>
<BButton variant="success" @click="showMe"> Show the Toast </BButton>
<BButton variant="danger" @click="hideMe"> Hide the Toast </BButton>
</BButtonGroup>
</template>
<script setup lang="ts">
import { BButton, BButtonGroup } from 'bootstrap-vue-next/components/BButton'
import { useToast } from 'bootstrap-vue-next/composables/useToast'
const { create } = useToast()
let toast: ReturnType<typeof create> | undefined
const getToast = () => {
toast ??= create({
title: 'Showing',
body: 'Toast is now showing',
variant: 'success',
position: 'bottom-center',
})
return toast
}
const showMe = async () => {
await getToast().show()
}
const hideMe = () => {
if (toast === undefined) return
toast.hide('programmatic-hide')
}
</script>Lifecycle and disposal
Created toast instances persist until you explicitly dispose them. Hiding a toast does not remove it from the orchestrator store.
const toast = create({title: 'Saved!'})
try {
await toast.show()
} finally {
await toast.destroy()
}You can also use the TypeScript await using syntax for automatic disposal when the scope exits.
await using toast = create({title: 'Saved!'})
await toast.show()Using promises
Hiding a Toast with promise
<template>
<BButtonGroup>
<BButton variant="success" @click="promiseToast"> Show the Toast </BButton>
</BButtonGroup>
</template>
<script setup lang="ts">
import { h, markRaw } from 'vue'
import { BButton, BButtonGroup } from 'bootstrap-vue-next/components/BButton'
import { useToast } from 'bootstrap-vue-next/composables/useToast'
const { create } = useToast()
const promiseToast = async () => {
await using r = await create({
variant: 'primary',
position: 'middle-center',
bodyClass: 'w-100',
modelValue: true,
slots: {
default: ({ hide }: { hide: (trigger?: string) => void }) => {
const yesButton = markRaw(
h(BButton, { onClick: () => hide('ok'), size: 'lg' }, () => 'Yes'),
)
const noButton = markRaw(
h(BButton, { onClick: () => hide('cancel'), size: 'lg' }, () => 'No'),
)
return [
markRaw(h('h2', { class: 'text-center mb-3' }, 'Ready?')),
markRaw(
h('div', { class: 'd-flex justify-content-center gap-2' }, [yesButton, noButton]),
),
]
},
},
options: {
resolveOnHide: true,
},
}).show()
if (r && typeof r === 'object' && 'ok' in r) {
await using _ = await create({ title: `you pressed: ${r.ok ? 'yes' : 'no'}` }).show()
}
}
</script>