Skip to content

useToast ​

useToast() is the injection shortcut for the toast store that a <Toaster> ancestor provides. Toasts are added through that store, not through component props, and the <Toaster> component owns the viewports that display them.

vue
<!-- App.vue -->
<script setup lang="ts">
import { Toaster } from '@myghf/ui'
import SaveButton from './SaveButton.vue'
</script>

<template>
  <Toaster>
    <SaveButton />
  </Toaster>
</template>
vue
<!-- SaveButton.vue -->
<script setup lang="ts">
import { useToast } from '@myghf/ui'

const toast = useToast() // resolves the store from the <Toaster> ancestor
</script>

<template>
  <button @click="toast.success('Saved', 'Your changes were saved.')">Save</button>
</template>

<Toaster> provides the store to its default slot, and useToast() resolves it from any descendant — nesting several components deep is fine.

Examples ​

useToast() in a descendant ​

The buttons below are rendered inside <Toaster>'s slot, so useToast() finds the store.

vue
<script setup lang="ts">
import { Toaster } from '@myghf/ui'
import ToastProviderButtons from './toast-provider-buttons.vue'
</script>

<template>
  <ClientOnly>
    <Toaster>
      <ToastProviderButtons />
    </Toaster>

    <template #fallback>
      <span class="text-sm text-muted">Loading toasts…</span>
    </template>
  </ClientOnly>
</template>
vue
<script setup lang="ts">
import { Button, useToast } from '@myghf/ui'

// This component is a descendant of <Toaster>, so the inject resolves.
const toast = useToast()
</script>

<template>
  <div class="flex flex-wrap gap-2">
    <Button size="sm" variant="outline" @click="toast.success('Saved', 'Your changes were saved.')">
      success()
    </Button>
    <Button size="sm" variant="outline" @click="toast.warning('Unsaved changes', 'Save before leaving.')">
      warning()
    </Button>
    <Button size="sm" variant="ghost" @click="toast.clear()">clear()</Button>
  </div>
</template>

createToastStore() with an explicit store ​

When the components that add toasts cannot be nested under <Toaster> — or the queue must outlive it — build a store with createToastStore() and pass it to <Toaster :store>.

vue
<script setup lang="ts">
import { Button, Toaster, createToastStore } from '@myghf/ui'

// A store you own, rather than the one <Toaster> would create.
const store = createToastStore({ max: 2, duration: 6000, position: 'bottom-end' })

let queued = 0

function queue() {
  queued += 1
  store.add({
    title: `Queued toast ${queued}`,
    description: 'max is 2, so extras wait until a slot frees up.',
    severity: 'success',
  })
}
</script>

<template>
  <ClientOnly>
    <Toaster :store="store" />

    <div class="flex flex-wrap gap-2">
      <Button size="sm" variant="outline" @click="store.info('Info', 'From an external store.')">
        store.info()
      </Button>
      <Button size="sm" variant="outline" @click="store.danger('Failed', 'Stored outside the Toaster.')">
        store.danger()
      </Button>
      <Button size="sm" @click="queue()">Queue one (max 2)</Button>
      <Button size="sm" variant="ghost" @click="store.clear()">store.clear()</Button>
    </div>

    <template #fallback>
      <span class="text-sm text-muted">Loading toasts…</span>
    </template>
  </ClientOnly>
</template>

useToast() ​

ts
import { useToast } from '@myghf/ui'

const toast = useToast() // call in setup, at the top level
  • Parameters: none.
  • Returns: the ToastStore provided by the nearest <Toaster> ancestor.
  • Throws: useToast() requires a <Toaster /> mounted above this component. when there is no provider in the current component tree.

useToast() is setup-only: it calls Vue's inject(toastKey), which must run during a component's setup. Do not call it inside an event handler, a watcher, or after an await. Call it once at the top of <script setup> and use the returned store everywhere.

createToastStore(options?) ​

Builds and returns a ToastStore directly. Use it when you need a store outside the <Toaster> tree, want to pre-seed or share one, or need control over the defaults.

OptionTypeDefaultDescription
maxnumber4Maximum toasts shown at once. Values below 1 clamp to 1.
durationnumber5000Default auto-dismiss delay in ms. 0 means persistent.
positionToastPosition'top-end'Default viewport corner.
ts
import { createToastStore } from '@myghf/ui'

const toast = createToastStore({ position: 'bottom-end', max: 3, duration: 0 })

The returned store's add() fills in the store defaults for any toast that omits them, so per-toast options always win. Pass the store to <Toaster :store="toast" /> (or to your own provider) so it has a viewport to render into. Rendering the store's methods without a <Toaster> shows nothing.

The ToastStore ​

MemberTypeDescription
itemsRef<ToastItem[]>Every toast, including those queued past max.
visibleComputedRef<ToastItem[]>The first max toasts still in the queue — the oldest ones — oldest first. Newer extras stay queued in items.
add(options: ToastOptions) => stringAdds a toast and returns its generated id.
remove(id: string) => voidRemoves the toast with that id; a queued toast moves up.
clear() => voidRemoves all toasts, queued ones included.
info(title: string, description?: string) => stringConvenience wrapper for severity: 'info'.
success(title: string, description?: string) => stringConvenience wrapper for severity: 'success'.
warning(title: string, description?: string) => stringConvenience wrapper for severity: 'warning'.
danger(title: string, description?: string) => stringConvenience wrapper for severity: 'danger'.
secondary(title: string, description?: string) => stringConvenience wrapper for severity: 'secondary'.

add() returns the toast id; keep it if you want to remove() that toast programmatically. The convenience methods are the same call with severity preset, so toast.success('Saved') is equivalent to toast.add({ title: 'Saved', severity: 'success' }).

ToastOptions ​

OptionTypeDefaultDescription
titlestring—Bold title line.
descriptionstring—Supporting body copy.
severityToastSeverity'info'Colour, icon, and live-region politeness.
durationnumberStore's durationAuto-dismiss delay in ms; 0 is persistent.
positionToastPositionStore's positionPer-toast viewport override.
closablebooleantrueShows the close button unless false.
iconstringSeverity defaultLucide icon name that overrides the severity icon.
action{ label: string; onClick: () => void }—Inline action button.

Severities ​

ToastSeverity is 'info' | 'success' | 'warning' | 'danger' | 'secondary'. Severity picks the tone and icon, and drives the live-region politeness: warning and danger are announced assertively (role="alert"), while info, success, and secondary are announced politely (role="status").

SeverityDefault iconRole
'info'infostatus
'success'circle-checkstatus
'warning'triangle-alertalert
'danger'circle-alertalert
'secondary'infostatus

The default icon and the tone classes come from toneClasses; pass icon to override the glyph. See the Toast component for the visual treatment.

Positions ​

ToastPosition is one of six logical corners:

top-start   top-center   top-end
bottom-start bottom-center bottom-end

Positions are logical: start and end mirror under RTL, while the centre positions stay centred. Set a default on the store (position) and override it per toast (add({ position })). The component page has a live positions demo.

Duration and queueing ​

  • duration is the auto-dismiss delay in milliseconds; the default is 5000.
  • duration: 0 makes a toast persistent until the user dismisses it or code calls remove()/clear(). Prefer it for important or actionable messages.
  • max caps how many toasts are visible at once. visible is the first max toasts still in the queue — the oldest ones — so newer extras stay in items as a queue and appear as the visible toasts dismiss. items holds everything, queued toasts included.
  • Adding past max never drops a toast — it waits in the queue.

toastKey ​

toastKey is the InjectionKey<ToastStore> used for the provide/inject pair. Reach for it only when you provide a store yourself — for example in tests or a custom shell — instead of going through <Toaster>:

ts
import { provide } from 'vue'
import { createToastStore, toastKey } from '@myghf/ui'

provide(toastKey, createToastStore())

Provider requirement ​

useToast() only works below a <Toaster>. The normal setup is to mount one near the root and put the app content in its default slot:

vue
<Toaster>
  <RouterView />
</Toaster>

A store created with createToastStore() still needs a <Toaster :store="store" /> to render. useToast() itself does not create a store; it throws rather than silently dropping toasts, so a missing provider fails loudly during development.

Released under the MIT License.