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.

View Source Edit this page on GitHub

Setup ​

To use useToast, you need one of the following setup approaches:

The easiest way is to wrap your application with the BApp component, which automatically sets up the orchestrator and registry:

vue
<template>
  <BApp>
    <RouterView />
  </BApp>
</template>

Plugin Setup (Legacy) ​

Alternatively, you can use the traditional plugin approach.

Basic Usage ​

Creating and showing a toast is simple:

HTML
vue
<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

HTML
vue
<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.

HTML
vue
<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.

HTML
vue
<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>

Globally Hiding Toasts ​

Toasts can also be hidden from anywhere in the app, without holding on to the controller that create returned. Give the toast an id, then hide it by that id:

HTML
vue
<template>
  <BButtonGroup>
    <BButton variant="success" @click="notify"> Show Toasts </BButton>
    <BButton variant="warning" @click="hide('user-request', 'session-expiring')">
      Hide Session Toast
    </BButton>
    <BButton variant="danger" @click="hideAll('route-change')"> Hide All </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, hide, hideAll } = useToast()

// Shows the toast, then disposes of it once it has been hidden
const show = async (payload: Parameters<typeof create>[0]) => {
  await using toast = create(payload)
  await toast.show()
}

const notify = () => {
  void show({
    id: 'session-expiring',
    title: 'Session expiring',
    body: 'You will be signed out soon',
    variant: 'warning',
    position: 'bottom-center',
  })
  void show({
    id: 'sync-complete',
    title: 'Sync complete',
    body: 'Everything is up to date',
    variant: 'success',
    position: 'bottom-center',
  })
}
</script>
  • hide: (trigger?: string, id?: ControllerKey) => void

    Hides the toast with the given id. The trigger is passed to the trigger property of the hide event and of the resolved show promise. When no id is given, every toast is hidden, just like hideAll

  • hideAll: (trigger?: string) => void

    Hides every toast that was created through the composable

hide(trigger, id) also hides a BToast that was declared in a template with a matching id. hideAll and hide without an id only cover the toasts that were created through the composable.

A toast that the orchestrator has not rendered yet is hidden through its store entry, so it never becomes visible. Such a toast reports modelValue as its trigger, since there is no component to run the hide cycle through. The same applies to a toast rendered through a custom component.

Hiding a toast does not remove it from the orchestrator store, see Lifecycle and disposal.

Lifecycle and disposal ​

Created toast instances persist until you explicitly dispose them. Hiding a toast does not remove it from the orchestrator store.

js
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.

js
await using toast = create({title: 'Saved!'})
await toast.show()

Using promises ​

Hiding a Toast with promise

HTML
vue
<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>