Quasar Notify is the plugin behind every toast in a Quasar app: "Saved", "Upload failed", "3 new messages". One call, $q.notify('Saved'), shows a message at the bottom of the screen that fades out after five seconds. Behind that call sit types, nine positions, a progress bar, action buttons, automatic grouping and app-wide defaults. This post walks through each one with the Quasar 2.35.2 API, and ends with an undo toast that gets one detail right that most versions get wrong.
Notify is a plugin, so it has to be registered. In a Quasar CLI app, add it to the Quasar config:
framework: {
plugins: ['Notify']
}With nuxt-quasar-ui, add 'Notify' to quasar.plugins in nuxt.config.ts. The Nuxt + Quasar setup guide shows the full config. If $q.notify is undefined, this step is missing.
Then call it from any component:
import { useQuasar } from 'quasar'
const $q = useQuasar()
$q.notify('Settings saved')
$q.notify({ message: 'Settings saved', caption: 'Just now', icon: 'check' })Outside a component (a Pinia store, an API wrapper, a router guard), import the plugin directly and call Notify.create({ ... }). It takes the same options.
Instead of picking colors and icons each time, pass a type:
$q.notify({ type: 'positive', message: 'Invoice sent' })
$q.notify({ type: 'negative', message: 'Payment failed. Check the card details.' })
$q.notify({ type: 'warning', message: 'Your trial ends in 3 days' })
$q.notify({ type: 'info', message: 'A new version is available' })Each type sets a color and an icon from the current icon set (warning also switches the text to dark for contrast). The fifth type, ongoing, is different: it shows a spinner, never times out, and can't be grouped. It is meant to be updated when the work finishes:
const notif = $q.notify({ type: 'ongoing', message: 'Exporting report...' })
await exportReport()
notif({ type: 'positive', message: 'Report ready', timeout: 2000 })$q.notify returns a function. Call it with an object to update the toast, or with no arguments to close it.
You can add your own types, or override the built-in ones, with Notify.registerType. Do it once, in a boot file (Quasar CLI) or a Nuxt plugin, not inside a component:
import { Notify } from 'quasar'
Notify.registerType('saved', {
icon: 'cloud_done',
color: 'grey-9',
textColor: 'white',
position: 'bottom-right',
timeout: 2000,
})position takes nine values: top-left, top, top-right, left, center, right, bottom-left, bottom (the default) and bottom-right.
There is no single right answer, but a few rules hold up:
bottom or bottom-right: close to the hand, out of the way of the content.top or top-right, near the header, where people look for status.top or bottom. The corner positions and left/right have little room on a narrow screen.Each position keeps its own stack: new toasts at a top position are added above the older ones, and at a bottom position below them.
timeout is in milliseconds and defaults to 5000. Set it to 0 to keep the toast until it is closed from code or by an action. progress: true adds a thin bar that shrinks as the time runs out, which tells the user how long they have to click an action:
$q.notify({
message: 'Message archived',
timeout: 4000,
progress: true,
})For an error the user must read, combine timeout: 0 with closeBtn: true (or a label such as closeBtn: 'Dismiss'), which adds a button that closes it.
actions is an array of QBtn props plus a handler. Clicking an action runs the handler and then closes the toast, unless the action has noDismiss: true:
$q.notify({
message: 'Jane invited you to Marketing',
multiLine: true,
actions: [
{ label: 'Decline', color: 'white', handler: () => declineInvite() },
{ label: 'Accept', color: 'yellow', handler: () => acceptInvite() },
],
})With more than one action, the toast switches to multiLine layout by default (actions under the message). Set multiLine: false to keep them on one line when they fit. Actions are flat buttons unless you say otherwise.
Click "Save" five times and you won't get five toasts. You get one, with a badge that says 5. Quasar builds a group key for each notification from its message, caption, multiLine setting, action labels and icons, and position. A new toast with the same key replaces the old one on screen, bumps the badge and restarts the timer.
That is usually what you want. You can change it:
group: false turns grouping off for that toast.group: 'sync-status' puts toasts in a named group even when their messages differ, so "Syncing..." and "Synced" replace each other instead of stacking.badgeColor, badgeTextColor, badgePosition and badgeClass style the badge.Grouping has one rule to remember: a grouped toast can't be updated. If you need the function that $q.notify returns to change the message later (a progress toast, for example), set group: false. The ongoing type does that for you.
Most apps want the same position and timeout everywhere. Set them once:
// a boot file in Quasar CLI, or a Nuxt plugin
import { Notify } from 'quasar'
Notify.setDefaults({
position: 'top-right',
timeout: 3000,
progress: true,
})You can also put defaults in the Quasar config under framework.config.notify, but the docs warn that actions with handlers only work through setDefaults, since the config file can't hold functions. For a one-off toast that should ignore the defaults, pass ignoreDefaults: true.
Deleting with an undo toast feels faster than a confirm dialog: the item disappears right away, and the user has a few seconds to change their mind. Here is a version that deletes on the server only if the user doesn't click Undo:
function deleteTask(task: Task) {
const index = tasks.value.indexOf(task)
tasks.value.splice(index, 1)
let undone = false
$q.notify({
message: `"${task.title}" deleted`,
group: false,
progress: true,
actions: [{ label: 'Undo', color: 'yellow', handler: () => { undone = true } }],
onDismiss: () => {
if (undone)
tasks.value.splice(index, 0, task)
else
api.deleteTask(task.id)
},
})
}Two details from the Notify source in 2.35.2 make this work:
onDismiss runs on every way out. When the timer ends, and also after an action's handler has run. So the Undo handler only sets a flag, and onDismiss decides what happens. Restoring inside the handler and deleting in onDismiss would do both.group: false is not optional here. Delete two tasks with the same title quickly and, with grouping on, the second toast replaces the first. The replaced toast is swapped out without being dismissed, so its onDismiss never runs and the first task is never deleted on the server. One toast per deletion avoids that.The trade-off: if the user closes the tab during those seconds, the server delete never happens and the task comes back on the next load. If that matters, delete on the server right away and make Undo call a restore endpoint instead. The toast code stays the same; only the two branches swap.
How do I close a notification from code? Keep the function that $q.notify returns and call it with no arguments.
Can I show HTML in a toast? Yes, with html: true, but the message is not sanitized, so never pass it user input.
How do I show a toast after a redirect? Call $q.notify after await router.push(...). Toasts live outside the page, so they survive the route change.
Why does my toast stay a second longer than its timeout? The source adds the show animation's duration (1000ms) to the timer, so a timeout: 2000 toast leaves after about three seconds.
The Notify Block has five notification designs built with Quasar, including undo, reply and allow/decline layouts. Two of them are free: preview them, copy the markup, and use them as the look for your own registerType toasts or as in-page alerts.