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.
Links
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
timeDurationisnull - Custom
elementsso 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:
- Install
- Call
notify.success('Saved') - Optionally
await confirm({ … }) - 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)
| Item | Why |
|---|---|
| Fix flex layout in package CSS (or stop using undeclared utility classes) | Prevents broken title / message / actions rows |
Guard window usage for SSR | Safe Nuxt imports without silent server crashes |
Publish real .d.ts for notify payloads and inject/$notify | TypeScript is table stakes for Vue 3 libs |
Document the flat notification object; remove nested options examples | Docs must match runtime |
P1 — Modern DX (Composition API first)
| Item | Why |
|---|---|
Export useNotificationCenter() / useNotify() | Matches how people write Vue 3 and Nuxt apps |
Keep $notify for Options API | Do 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
| Item | Why |
|---|---|
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 confirms | Usable with assistive tech |
| Ship SVG / CSS icons by default; make FA / Material optional | No empty icon holes |
P3 — Nuxt module and lighter core
| Item | Why |
|---|---|
| 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 possible | Smaller install, faster cold start |
| Avoid bundler build of Vue inside inject helpers; prefer slots / render functions | Cleaner peer dependency story |
| Tree-shakeable ESM entry points | Import only what you use |
P4 — Quality bar for strangers
| Item | Why |
|---|---|
| Vitest coverage for merge, timer, destroy, confirm settle | Regressions should fail CI |
| Migration guide 2.x → 3.x | Community trust |
| Minimal CodeSandbox / StackBlitz demo | Try before install |
| Changelog that speaks in user outcomes | Not only commit subjects |
Suggested public API sketch
Not final — just the shape I want the community docs to teach:
// 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',
})
// Plugin install still works
app.use(NotificationCenter, {
position: 'topRight',
timeDuration: 4000,
})
/* 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.