Post

Improving Vue Notification Center for the community

Improving Vue Notification Center for the community

Earlier I wrote about the internals of Vue Notification Center — the folder layout, the store, the custom elements idea. That post was about how it is built.

This one is about how it feels when you actually use it.

I recently wired @harianto/vue-notification-center into this Nuxt site: toasts instead of banners and alert(), and a Promise-based confirm built on custom notification elements. Dogfooding is useful. It is also slightly uncomfortable when the library under review is your own.

Here is the honest readout — and a plan to improve the package so other people can adopt it without fighting the same sharp edges.

What still works

The core idea is sound:

  • A small reactive store with addNotification / removeNotification / destroy
  • Types (info, success, warning, danger, error) and six corner positions
  • Timed auto-dismiss plus sticky notices when timeDuration is null
  • Custom elements so a toast can carry more than a title and a string
  • An Alert Center path for notices that should not behave like ephemeral toasts

For success / error / info feedback, it fits this site well. A thin useNotify() wrapper around $notify is enough for day-to-day UX.

What dogfooding exposed

1. Layout utilities that do not exist

Notification.vue marks the card with Tailwind-like classes such as flex flex-col. Those utilities are not shipped in the package CSS. In a Nuxt app that happens to generate .flex but not .flex-col, the card becomes a horizontal flex row.

Title, message, and custom action buttons then sit side by side. Confirms looked broken: narrow wrapped copy in the middle, Cancel and Delete stacked on the right. The fix in the consuming app was an explicit column layout override. The real fix belongs in the library.

2. Confirms are the wrong metaphor for a toast

A toast is ambient: it informs, then leaves. A delete or leave with unsaved changes prompt is a decision. Decisions need:

  • Clear hierarchy (title → consequence → actions)
  • Predictable focus
  • Escape / cancel paths
  • Ideally a modal backdrop so the rest of the UI does not compete

Building confirm buttons with document.createElement and injecting them via elements works. It is also a workaround. Consumers should not have to invent a11y and layout every time they need “Are you sure?”.

3. Options API drift between docs and runtime

Examples sometimes nest settings under options: { timeDuration: null }. The runtime merges with validateKeys against the default notification shape, so unknown keys (including a nested options object) get stripped. Flat fields like timeDuration and elementClass are what actually apply.

That mismatch is the kind of thing that burns an afternoon for a first-time user.

4. SSR and Nuxt friction

The bundle assigns to window.notificationCenter at module evaluation time. That forces a client-only plugin in Nuxt. Fine once you know it; surprising when you do not.

The README says “Nuxt 3 ready”, but the path is still: install → write a .client plugin → import CSS → mount <NotificationCenter /> yourself. A Nuxt module would match the claim better.

5. TypeScript and packaging gaps

package.json advertises a types entry pointing at vue-notification-center.d.ts. In the published package that file is easy to miss (or absent from dist). Without solid types, Composition API usage becomes guesswork and cast soup.

6. Accessibility and peer UI assumptions

Close controls lean on Font Awesome classes. Icon asides lean on Material Icons. If the host app does not load those fonts, you get empty chrome. Toasts also need proper live regions (role="status" / role="alert") so screen readers announce them.

7. Bundle weight for a toast library

Dayjs, uuid, deepmerge, and vue/dist/vue.esm-bundler for element injection are a lot of machinery for “show a message for four seconds”. Lighter defaults would make the package easier to justify in production apps.

Design principle going forward

Keep the strengths (positions, timers, typed notices, extensibility).

Raise the floor so a Vue / Nuxt developer can:

  1. Install
  2. Call notify.success('Saved')
  3. Optionally await confirm({ … })
  4. Theme with CSS variables

…without reading the source or patching layout bugs.

Roadmap: make it community-ready

This is the improvement plan I want to execute for a cleaner major release (working title: v3).

P0 — Stop the bleed (must ship first)

ItemWhy
Fix flex layout in package CSS (or stop using undeclared utility classes)Prevents broken title / message / actions rows
Guard window usage for SSRSafe Nuxt imports without silent server crashes
Publish real .d.ts for notify payloads and inject/$notifyTypeScript is table stakes for Vue 3 libs
Document the flat notification object; remove nested options examplesDocs must match runtime

P1 — Modern DX (Composition API first)

ItemWhy
Export useNotificationCenter() / useNotify()Matches how people write Vue 3 and Nuxt apps
Keep $notify for Options APIDo not break existing consumers
Flatten and name options consistently (timeDuration, disableClose, showCloseButton)One vocabulary in code and docs
CSS custom properties for theming (--nc-bg, --nc-ink, --nc-accent-*)Apps theme without 300-line override sheets

P2 — Confirms and accessibility

ItemWhy
First-class confirm() that returns Promise<boolean>Stop inventing DOM buttons per app
Implement confirm as a Vue component (focus trap, Escape, labelled buttons)Toasts stay for feedback; confirms get decision UX
Live regions and keyboard behaviour for toasts and confirmsUsable with assistive tech
Ship SVG / CSS icons by default; make FA / Material optionalNo empty icon holes

P3 — Nuxt module and lighter core

ItemWhy
Official Nuxt module: plugin, CSS, auto-import composables, optional auto-mount“Nuxt ready” should mean minutes, not a scavenger hunt
Replace dayjs / uuid / deepmerge with smaller primitives where possibleSmaller install, faster cold start
Avoid bundler build of Vue inside inject helpers; prefer slots / render functionsCleaner peer dependency story
Tree-shakeable ESM entry pointsImport only what you use

P4 — Quality bar for strangers

ItemWhy
Vitest coverage for merge, timer, destroy, confirm settleRegressions should fail CI
Migration guide 2.x → 3.xCommunity trust
Minimal CodeSandbox / StackBlitz demoTry before install
Changelog that speaks in user outcomesNot only commit subjects

Suggested public API sketch

Not final — just the shape I want the community docs to teach:

ts
// Composable (Vue 3 / Nuxt)
const { notify, success, error, warning, info, confirm } = useNotificationCenter()

success('Post saved')
error('Save failed')

const ok = await confirm({
  title: 'Delete post?',
  message: 'This cannot be undone.',
  confirmLabel: 'Delete post',
  type: 'danger',
})
ts
// Plugin install still works
app.use(NotificationCenter, {
  position: 'topRight',
  timeDuration: 4000,
})
css
/* Theme without fighting component CSS */
:root {
  --nc-bg: #202127;
  --nc-ink: #dfdfd6;
  --nc-ink-muted: #98989f;
  --nc-accent-success: #3d9b6a;
  --nc-accent-danger: #c44b4b;
}

Custom elements can stay for power users. The happy path should not require them.

What stays in scope (and what does not)

In scope for v3

  • Toasts, positions, types, theming, confirm helper, Nuxt module, types, a11y baseline

Out of scope (for now)

  • Full design-system button kits
  • Server-pushed / websocket notification inboxes (Alert Center can grow later)
  • Replacing every app’s modal system

A good library has a sharp boundary. Notification Center should be excellent at ephemeral feedback and lightweight confirms — not a general overlay framework.

How you can help

If you use (or tried) the package:

  • Open an issue with a minimal reproduction — especially layout, SSR, or typing problems
  • Say whether you need toasts only, or toasts + confirm
  • Share which icon set (or none) you already ship in your app

I would rather fix the foundation once than ask every consumer to paper over the same cracks.

Closing

Writing the architecture tour was the easy part. Using the library on a real Nuxt site was the exam.

The exam grade today: solid idea, unfinished product surface.

The plan above is how I want to turn Vue Notification Center into something the community can install without reading my source code — and something I can keep recommending without a caveat in every sentence.

If you want to follow along, watch the GitHub repo. The next useful release is the boring one: types, layout CSS, SSR safety, and docs that match the runtime.