zenith-notify
v0.1.0
Published
The React Notification Operating System — a beautiful, intelligent, framework-agnostic notification platform. Smart dedupe, grouping, priority queues, multi-step workflows, a full notification center, theming and accessibility out of the box.
Maintainers
Readme
◈ zenith-notify
The React Notification Operating System.
Smart dedupe · priority queues · multi-step workflows · a full notification center · 7 built-in themes — all in one tiny, type-safe, framework-agnostic package.
~2.8 KB core · ~9 KB full (gzipped) · zero dependencies · SSR / RSC / Edge safe
Why zenith-notify?
Most toast libraries stop at "show a message and fade it out." zenith-notify is a complete notification platform:
| Capability | Sonner | React Toastify | Notistack | zenith-notify |
| --- | :---: | :---: | :---: | :---: |
| Beautiful defaults | ✅ | ⚠️ | ⚠️ | ✅ |
| Duplicate collapsing (×3) | ❌ | ❌ | ⚠️ | ✅ |
| Priority queue + overflow | ❌ | ⚠️ | ✅ | ✅ |
| Swipe to dismiss | ✅ | ✅ | ❌ | ✅ |
| Height-collapse stacking | ✅ | ⚠️ | ⚠️ | ✅ |
| Multi-step workflow engine | ❌ | ❌ | ❌ | ✅ |
| Live-updating notifications | ⚠️ | ⚠️ | ⚠️ | ✅ |
| Built-in notification center | ❌ | ❌ | ❌ | ✅ |
| History + persistence | ❌ | ❌ | ❌ | ✅ |
| Offline awareness | ❌ | ❌ | ❌ | ✅ |
| Cross-tab sync | ❌ | ❌ | ❌ | ✅ |
| 7 first-class themes | ⚠️ | ⚠️ | ⚠️ | ✅ |
| Framework-agnostic core | ❌ | ❌ | ❌ | ✅ |
| Full TS inference | ✅ | ⚠️ | ⚠️ | ✅ |
Installation
npm install zenith-notify// 1. Import the stylesheet once (e.g. in your root layout)
import "zenith-notify/styles.css";
// 2. Mount one <Toaster /> near the root of your app
import { Toaster } from "zenith-notify";
export default function App() {
return (
<>
<YourApp />
<Toaster theme="zenith" placement="bottom-right" />
</>
);
}// 3. Fire notifications from anywhere — no context, no prop drilling
import { toast } from "zenith-notify";
toast.success("Changes saved");
toast.error("Something went wrong");Core API
Basics
toast("Plain message");
toast.success("Saved", { description: "Your changes are live." });
toast.error("Upload failed");
toast.warning("Storage almost full");
toast.info("A new update is available");
toast.loading("Processing…");Every helper accepts either a message or a full options object:
toast.success({
title: "Payment received",
description: "$249.00 from Acme Inc.",
duration: 6000,
priority: "high",
actions: [{ label: "View receipt", variant: "primary", onClick: () => open() }],
});Rich types
upload · download · deploy · build · payment · subscription · chat · ai · reminder · calendar · meeting · sync · auth · permission · verification · security · achievement · system · server · webhook · stream · task — each ships with a meaningful icon and accent color.
toast.payment({ title: "Payment received", description: "$249.00 from Acme Inc." });
toast.achievement({ title: "Achievement unlocked!", description: "100 deploys this month." });Promise engine
toast.promise(saveUser(), {
loading: "Saving…",
success: (user) => `Saved ${user.name}`,
error: (err) => `Failed: ${err.message}`,
});Multi-step workflows
Go far beyond loading → success → error. Model real pipelines that update in place:
const flow = toast.workflow(
[
{ id: "build", label: "Building project" },
{ id: "upload", label: "Uploading assets" },
{ id: "restart", label: "Restarting server" },
{ id: "live", label: "Going live" },
],
{ title: "Deploying to production", type: "deploy" },
);
flow.complete("build", "1,204 modules");
flow.complete("upload", "42 files · 8.1 MB");
flow.complete("restart");
flow.complete("live");
flow.done("Deployment live 🎉");Live updates & progress
const id = toast.upload({
title: "Uploading photos",
duration: false,
progress: { value: 0, count: "0 / 5 files", eta: "calculating…" },
});
toast.update(id, { progress: { value: 60, count: "3 / 5 files", eta: "ETA 2s" } });
toast.update(id, { type: "success", title: "5 files uploaded", progress: undefined });Interactive notifications
toast.info({
title: "New login detected",
description: "A new device signed in from San Francisco, CA.",
type: "security",
duration: false,
actions: [
{ label: "It was me", variant: "primary", onClick: () => trust() },
{
label: "Secure account",
variant: "danger",
autoDisableWhilePending: true,
onClick: async () => { await lockAccount(); },
},
],
});Fully custom render
toast.custom({
duration: false,
render: ({ notification, dismiss }) => <MyComponent onClose={dismiss} />,
});Use cases & recipes
zenith-notify is designed to cover the full spectrum of real-world notification needs — from a one-line success toast to a multi-stage background pipeline. Here are the patterns it's built for.
1. Forms & CRUD feedback
Confirm saves, deletes, and validation errors — the everyday bread and butter.
async function onSubmit(values) {
toast.promise(api.saveProfile(values), {
loading: "Saving your profile…",
success: "Profile updated",
error: (e) => `Couldn't save: ${e.message}`,
});
}
// Optimistic delete with undo
function onDelete(item) {
const removed = removeLocally(item);
toast.warning({
title: `Deleted "${item.name}"`,
duration: 6000,
actions: [{ label: "Undo", variant: "primary", onClick: () => restore(removed) }],
});
}2. File uploads & downloads with live progress
A single sticky toast that mutates in place as bytes move.
const id = toast.upload({
title: "Uploading video",
duration: false,
progress: { value: 0, count: "0 MB", eta: "calculating…" },
});
uploader.on("progress", (p) =>
toast.update(id, { progress: { value: p.percent, count: `${p.mb} MB`, eta: p.eta } }),
);
uploader.on("done", () =>
toast.update(id, { type: "success", title: "Upload complete", progress: undefined }),
);
uploader.on("error", () => toast.update(id, { type: "error", title: "Upload failed" }));3. Deploys, builds & CI pipelines
Model a real multi-stage job that updates step-by-step instead of firing five separate toasts.
const flow = toast.workflow(
[
{ id: "install", label: "Installing dependencies" },
{ id: "build", label: "Building" },
{ id: "test", label: "Running tests" },
{ id: "deploy", label: "Deploying" },
],
{ title: "CI · main → production", type: "deploy", duration: false },
);
pipeline.on("stage", (s, detail) => flow.complete(s, detail));
pipeline.on("finished", () => flow.done("Shipped to production 🚀"));
pipeline.on("failed", (s) => flow.fail("Pipeline failed at " + s));4. Payments, subscriptions & billing
High-priority, receipt-style notifications with actions.
toast.payment({
title: "Payment received",
description: "$249.00 from Acme Inc.",
priority: "high",
actions: [{ label: "View receipt", variant: "primary", onClick: openReceipt }],
});
toast.subscription({ title: "Plan upgraded to Pro", description: "Billed annually." });
toast.warning({ title: "Card expiring soon", priority: "high", actions: [{ label: "Update", onClick: openBilling }] });5. Security & auth events
Critical alerts that never auto-dismiss and demand a decision.
toast.security({
title: "New sign-in detected",
description: "Chrome on macOS · San Francisco, CA",
priority: "critical",
duration: false,
actions: [
{ label: "It was me", variant: "primary", onClick: trustDevice },
{ label: "Lock account", variant: "danger", autoDisableWhilePending: true, onClick: () => lockAccount() },
],
});
toast.verification({ title: "Email verified" });
toast.permission({ title: "Camera access granted" });6. Realtime: chat, streams & collaboration
Live pings from websockets — deduped so a chatty channel doesn't bury the screen.
socket.on("message", (m) =>
toast.chat({ title: m.author, description: m.text, groupKey: m.channelId }),
);
socket.on("user-joined", (u) => toast.info(`${u.name} joined the call`, { priority: "low" }));
socket.on("stream-live", (c) => toast.stream({ title: `${c.name} is live`, actions: [{ label: "Watch", onClick: () => open(c) }] }));7. AI assistant & agent status
Show streaming/thinking states that resolve into a result.
const id = toast.ai({ title: "Generating summary…", duration: false });
await stream((chunk) => toast.update(id, { description: chunk }));
toast.update(id, { type: "success", title: "Summary ready", duration: 4000 });8. Background sync & offline-first apps
Reflect connectivity and queued work automatically.
<Toaster crossTab persist showOfflineBanner />toast.sync({ title: "Syncing changes…", duration: false, id: "sync" });
window.addEventListener("online", () => toast.update("sync", { type: "success", title: "All changes synced" }));9. Reminders, calendar & scheduling
toast.reminder({ title: "Standup in 5 minutes", actions: [{ label: "Join", variant: "primary", onClick: joinCall }] });
toast.meeting({ title: "Design review", description: "Starting now · Room 4" });
toast.calendar({ title: "Event added to your calendar" });10. System, server & webhook events
For dashboards, admin panels, and DevOps tooling.
toast.server({ title: "CPU at 92%", priority: "high", type: "warning" });
toast.webhook({ title: "Stripe webhook received", description: "invoice.paid" });
toast.system({ title: "Maintenance window tonight, 2–3 AM UTC", priority: "low" });11. Gamification & achievements
toast.achievement({ title: "Achievement unlocked!", description: "7-day streak 🔥", priority: "high" });
toast.task({ title: "3 of 5 onboarding steps complete" });12. A full notification center (inbox)
When toasts aren't enough, keep a persistent, searchable history with unread counts.
import { NotificationCenter } from "zenith-notify";
<NotificationCenter theme="zenith" />; // Ctrl/⌘ + J to toggle13. Non-React / cross-framework usage
Drive notifications from the framework-agnostic core in React Native, Vue, Solid, workers, or plain JS.
import { NotificationStore } from "zenith-notify/core";
const store = new NotificationStore({ maxVisible: 3 });
store.subscribe(() => render(store.getSnapshot()));
store.add({ title: "Works anywhere", type: "success" });Rule of thumb: transient feedback →
toast.*; long-running work →toast.workflow/ livetoast.update; things the user must be able to revisit → the notification center withpersist.
Smart features
- Duplicate detection — identical notifications collapse into one with a live
×Nbadge instead of spamming the screen. - Priority queue — only
maxVisibletoasts render at once; the rest wait, sorted by priority (critical›high›normal›low›silent).criticalalways gets a slot. - Swipe to dismiss — drag a toast toward its edge to fling it away (horizontal for side placements, vertical for centered), with rubber-band resistance the other way.
- Height-collapse stacking — dismissing a toast animates its height to zero so the rest of the stack glides up smoothly instead of jumping.
- Auto-pause — timers pause on hover/focus/drag and when the tab is hidden.
- Offline awareness — an offline banner appears when the connection drops.
- Cross-tab sync — opt in with
crossTabto mirror dismiss/read state across tabs.
<Toaster maxVisible={4} dedupe crossTab persist />Notification center
A complete, keyboard-accessible center with history, unread badge, search, filters, pin & favorite:
import { NotificationCenter } from "zenith-notify";
<NotificationCenter theme="zenith" />; // toggle with Ctrl/⌘ + JHooks
import {
useNotificationCenter, // { items, unread, markRead, pin, favorite, clear, ... }
useUnread, // number
useOnline, // boolean
useQueue, // { visible, queued, max }
useVisibleNotifications,
useStore, // (selector) => T — build your own UI
} from "zenith-notify";All hooks read from a stable useSyncExternalStore snapshot, so they only re-render when their slice actually changes.
Theming
Seven first-class themes: zenith · apple · github · material · discord · glass · minimal.
<Toaster theme="apple" colorScheme="system" />Themes are pure design tokens compiled to CSS variables (--zn-*), so you can override any of them in your own CSS with zero JS.
.zn-toaster { --zn-accent: #ff5e5b; --zn-radius: 20px; }<Toaster /> props
| Prop | Type | Default |
| --- | --- | --- |
| placement | top-left \| top-center \| top-right \| bottom-left \| bottom-center \| bottom-right | bottom-right |
| theme | ThemeName | zenith |
| colorScheme | light \| dark \| system | system |
| animation | slide \| fade \| scale \| flip \| bounce \| none | slide |
| maxVisible | number | 4 |
| duration | number (ms) | 4000 |
| dedupe | boolean | true |
| persist | boolean | false |
| crossTab | boolean | false |
| showOfflineBanner | boolean | true |
| width | number (px) | 380 |
Framework-agnostic core
The zenith-notify/core entry ships the renderer-free NotificationStore (dedupe, queue, timers, history, promise/workflow engine). Build adapters for React Native, Vue, Solid, or anything else on top of it:
import { NotificationStore } from "zenith-notify/core";
const store = new NotificationStore({ maxVisible: 3 });
const unsubscribe = store.subscribe(() => render(store.getSnapshot()));
store.add({ title: "Hello", type: "success" });Accessibility
role="status"/role="alert"witharia-livescaled by priority (assertiveforcritical).- Full keyboard support —
Escdismisses, focus pauses timers. - Swipe/pointer gestures never hijack clicks on buttons, links, or inputs inside a toast.
- Respects
prefers-reduced-motion. - WCAG-conscious contrast across every theme.
SSR / RSC / Edge
- The bundle carries the
"use client"directive, so it drops straight into the Next.js App Router. <Toaster />rendersnulluntil mounted — hydration-safe by construction.- No Node built-ins; runs on Edge and in React Native web.
Development
npm install # install deps
npm run demo # launch the interactive demo (Vite) on :5180
npm test # run the Vitest suite
npm run typecheck # type-check the library
npm run build # produce dist/ (ESM + CJS + d.ts + styles.css)The demo/ folder is a full playground exercising every feature — themes, placements, animations, workflows, the promise engine, smart dedupe/queue, interactive actions, and the notification center.
Developer documentation
- CONTRIBUTING.md — setup, scripts, conventions, adding a type, releasing.
- docs/ARCHITECTURE.md — how the core engine and React layer fit together, plus the invariants not to break.
- docs/API.md — the complete public API reference (
toast, components, hooks,NotificationStore, and all types).
License
MIT
