useNotifications
Notification lifecycle management with severity levels, state mutations, toast queuing, and auto-dismiss.
Installation
Install the Notifications plugin in your app’s entry point:
import { createApp } from 'vue'
import { createNotificationsPlugin } from '@vuetify/v0'
import App from './App.vue'
const app = createApp(App)
app.use(createNotificationsPlugin())
app.mount('#app')Usage
Once the plugin is installed, use the useNotifications composable in any component:
<script setup lang="ts">
import { useNotifications } from '@vuetify/v0'
const notifications = useNotifications()
function onSave () {
notifications.send({
subject: 'Changes saved',
severity: 'success',
timeout: 3000,
})
}
function onError () {
notifications.send({
subject: 'Build failed',
severity: 'error',
timeout: -1,
})
}
</script>
<template>
<button @click="onSave">
Save
</button>
</template>Architecture
createNotifications layers notification semantics on top of the registry and queue primitives, with plugin installation via createPluginContext:
Severity Levels
The severity field categorizes notifications by urgency. It maps to ARIA live region roles automatically:
| Value | ARIA role | Use for |
|---|---|---|
'error' | role="alert" | Failures, errors, destructive outcomes |
'warning' | role="alert" | Degraded state, approaching limits |
'info' | role="status" | Neutral updates, background activity |
'success' | role="status" | Completed actions, positive outcomes |
NotificationSeverity is extensible — custom values like 'critical' are accepted with autocomplete for the four defaults.
API
| Method | Description |
|---|---|
send(input) | Create notification + enqueue for toast display |
register(input) | Create notification in registry only (no toast). Use for historical items |
queue | Queue context — queue.values(), queue.pause(), queue.resume() |
read(id) / unread(id) | Toggle read state |
seen(id) | Mark as seen |
archive(id) / unarchive(id) | Toggle archive state |
snooze(id, until) / wake(id) | Snooze with expiry |
readAll() / archiveAll() | Bulk operations |
onboard(items) | Bulk-register enriched notifications into registry (no toast) |
clear() | Remove all notifications from the registry |
dispose() | Tear down event listeners and clear the registry |
Examples
Adapters
Adapters let you swap the underlying notification service without changing your application code.
| Adapter | Import | Description |
|---|---|---|
KnockNotificationsAdapter | @vuetify/v0/notifications | Knock↗︎ integration |
NovuNotificationsAdapter | @vuetify/v0/notifications | Novu↗︎ integration |
Knock
Knock↗︎ is a notification infrastructure platform with feeds, preferences, and multi-channel delivery. Install their JavaScript SDK↗︎ to get started. Supports both inbound (feed → notifications) and outbound (read/archive → Knock API).
pnpm add @knocklabs/clientnpm install @knocklabs/clientyarn add @knocklabs/clientbun add @knocklabs/clientimport { createApp } from 'vue'
import { createNotificationsPlugin } from '@vuetify/v0'
import { KnockNotificationsAdapter } from '@vuetify/v0/notifications'
import { feed } from './plugins/knock'
import App from './App.vue'
const app = createApp(App)
app.use(
createNotificationsPlugin({
adapter: new KnockNotificationsAdapter(feed),
})
)
app.mount('#app')import Knock from '@knocklabs/client'
export const knock = new Knock(import.meta.env.VITE_KNOCK_PUBLIC_KEY)
knock.authenticate(import.meta.env.VITE_KNOCK_USER_ID)
export const feed = knock.feeds.initialize(
import.meta.env.VITE_KNOCK_FEED_CHANNEL_ID
)Novu
Novu↗︎ is an open-source notification infrastructure with in-app feeds, digests, and multi-channel delivery. Install their JavaScript SDK↗︎ to get started. Supports both inbound (feed → notifications) and outbound (read/unread/seen/archive/unarchive → Novu API).
The adapter maps Novu severity strings to NotificationSeverity by default: critical/high → error, medium → warning, low → info. Pass a custom severity function to override.
pnpm add @novu/jsnpm install @novu/jsyarn add @novu/jsbun add @novu/jsimport { createApp } from 'vue'
import { createNotificationsPlugin } from '@vuetify/v0'
import { NovuNotificationsAdapter } from '@vuetify/v0/notifications'
import { novu } from './plugins/novu'
import App from './App.vue'
const app = createApp(App)
app.use(
createNotificationsPlugin({
adapter: new NovuNotificationsAdapter(novu),
})
)
app.mount('#app')import { Novu } from '@novu/js'
export const novu = new Novu({
subscriberId: import.meta.env.VITE_NOVU_SUBSCRIBER_ID,
applicationIdentifier: import.meta.env.VITE_NOVU_APP_ID,
})Custom Adapters
Extend NotificationsAdapter to connect any backend:
import { NotificationsAdapter } from '@vuetify/v0/notifications'
import type { NotificationsAdapterContext } from '@vuetify/v0'
class MyBackendAdapter extends NotificationsAdapter {
setup (context: NotificationsAdapterContext) {
// Wire inbound: push notifications into the registry
myBackend.onMessage(msg => {
context.send({ id: msg.id, title: msg.title, body: msg.body })
})
// Wire outbound: sync read/archive actions back to the backend
context.on('notification:read', (data: any) => {
myBackend.markRead(data.id)
})
}
dispose () {
myBackend.disconnect()
}
}
app.use(createNotificationsPlugin({ adapter: new MyBackendAdapter() }))Adapter context methods:
| Method | Purpose |
|---|---|
send(input) | Register and enqueue for toast display (real-time inbound) |
register(input) | Register in history only — no toast (initial/historical load) |
on(event, handler) | Subscribe to outbound lifecycle events |
off(event, handler) | Unsubscribe from a lifecycle event |
Custom Ticket Fields
Extend NotificationInput to add domain-specific fields. Pass the type parameter through the adapter and plugin:
import { NotificationsAdapter } from '@vuetify/v0/notifications'
import type { NotificationInput, NotificationsAdapterContext } from '@vuetify/v0'
interface AppNotification extends NotificationInput {
priority: 'low' | 'medium' | 'high'
imageUrl?: string
}
class MyBackendAdapter extends NotificationsAdapter<AppNotification> {
setup (context: NotificationsAdapterContext<AppNotification>) {
myBackend.onMessage(msg => {
context.send({
id: msg.id,
subject: msg.title,
priority: msg.priority, // custom field
imageUrl: msg.imageUrl, // custom field
})
})
}
}
app.use(createNotificationsPlugin<AppNotification>({ adapter: new MyBackendAdapter() }))Custom fields are preserved on the ticket and accessible anywhere you inject the notifications context.
Functions
createNotifications
(options?: NotificationsOptions) => NotificationsContext<NotificationInput, NotificationTicket<NotificationInput>>createNotificationsContext
<_E>(_options?: NotificationsPluginOptions | undefined) => ContextTrinity<_E>createNotificationsPlugin
(_options?: NotificationsPluginOptions | undefined) => PluginuseNotifications
<_E>(namespace?: string) => _EOptions
Properties
queue
QueueContext<QueueTicketInput<unknown>, QueueTicket<QueueTicketInput<unknown>>>The toast display queue. Access `queue.values()`, `queue.pause()`, `queue.resume()`.
Methods
seek
(direction?: "first" | "last", from?: number, predicate?: (ticket) => boolean) => E | undefinedSeek for a ticket based on direction and optional predicate
on
<K extends Extensible<RegistryEventName>>(event: K, cb: RegistryEventCallback<E, K>) => voidListen for registry events
off
<K extends Extensible<RegistryEventName>>(event: K, cb: RegistryEventCallback<E, K>) => voidStop listening for registry events
emit
<K extends Extensible<RegistryEventName>>(event: K, data: EventPayload<E, K>) => voidEmit an event with data
batch
<R>(fn: () => R) => RExecute operations in a batch, deferring cache invalidation and event emission until complete