Primitives for the browser Notifications API with reactive permission management
| Stage | Category | Version | Last Updated | Demo |
|---|---|---|---|---|
| 3 | Browser APIs | 1.0.0-next.3 (next) | Aug 12, 2026 | Demo → |
npm i @solid-primitives/notification@nextPrimitives for the browser Notifications API with reactive permission management.
isNotificationSupported— SSR-safe check for Notifications API availability.makeNotification— Non-reactive helper returning[show, close]. No Solid lifecycle dependency.createNotification— Reactive primitive that tracks the liveNotificationinstance and cleans up on owner disposal.createNotificationPermission— Reactive permission manager that exposes a live permission signal and arequestPermissionfunction.
How to use it
isNotificationSupported
Returns true when the Notifications API is available. Always false on the server.
import { isNotificationSupported } from "@solid-primitives/notification";
if (isNotificationSupported()) { console.log("notifications available");}makeNotification
Non-reactive helper with no Solid lifecycle dependency. Both returned functions are no-ops when the API is unavailable.
show() returns null when Notification.permission is not "granted" — use createNotificationPermission to request permission first.
Because makeNotification has no reactive owner, cleanup is the caller's responsibility. Inside a reactive scope, register close with onCleanup:
import { onCleanup } from "solid-js";import { makeNotification } from "@solid-primitives/notification";
const [show, close] = makeNotification("New message", { body: "Hello!" });
// Register cleanup with the current reactive owneronCleanup(close);
button.addEventListener("click", () => show());
// Or close programmatically at any timeclose();Outside a reactive scope (e.g. in plain event handlers), call close() directly when done.
createNotification
Reactive primitive tied to the current reactive owner.
titleandoptionscan be plain values or reactive accessors — their current values are read each timeshow()is called.notificationis a reactiveAccessor<Notification | null>that reflects the live instance, updating tonullwhen the notification is dismissed (either programmatically or by the OS).- The notification is automatically closed when the reactive owner is disposed.
- Pass an optional
handlersobject to respond to notification events.
import { createEffect } from "solid-js";import { createNotification } from "@solid-primitives/notification";
const { show, close, notification, supported } = createNotification( () => `You have ${unread()} messages`, { icon: "/icon.png" }, { onClick: n => { window.focus(); }, onClose: n => { console.log("dismissed"); }, onError: n => { console.error("notification failed"); }, },);
// Show a notification (reads reactive title at call time)show();
// React to visibility changescreateEffect(() => { if (notification()) console.log("notification visible"); else console.log("notification gone");});
// Close programmaticallyclose();createNotificationPermission
Reactive permission manager built on the browser Permissions API.
The permission accessor reflects the live permission state and updates automatically whenever it changes — including after requestPermission() resolves or the user edits their browser settings directly.
Permission values follow Permissions API vocabulary: "granted", "denied", "prompt" (not yet asked), or "unknown" while the initial async query is still resolving. Note that the Notifications API uses "default" for the same concept that the Permissions API calls "prompt".
On the server or when the API is unavailable, permission always returns "unknown" and requestPermission resolves immediately without effect.
import { Show } from "solid-js";import { createNotificationPermission } from "@solid-primitives/notification";
const { permission, requestPermission } = createNotificationPermission();
// Gate UI on permission state<Show when={permission() !== "granted"}> <button onClick={requestPermission}>Enable notifications</button></Show>
// Call without expecting a return value — permission() updates reactively after it resolvesrequestPermission();Full example
import { Component, Show } from "solid-js";import { createNotification, createNotificationPermission, isNotificationSupported,} from "@solid-primitives/notification";
const NotificationDemo: Component = () => { const { permission, requestPermission } = createNotificationPermission(); const { show, close, notification } = createNotification( "Solid Primitives", { body: "Hello from SolidJS!" }, { onClick: () => window.focus() }, );
return ( <Show when={isNotificationSupported()} fallback={<p>Not supported</p>}> <p>Permission: {permission()}</p> <p>Active: {notification() ? "yes" : "no"}</p> <Show when={permission() !== "granted"}> <button onClick={requestPermission}>Request permission</button> </Show> <button onClick={() => show()}>Show</button> <button onClick={close}>Close</button> </Show> );};Types
/** Event handler callbacks for `createNotification`. */type NotificationEventHandlers = { /** Called when the user clicks the notification. */ onClick?: (notification: Notification) => void; /** Called when the notification is dismissed, whether by the user, the OS, or `close()`. */ onClose?: (notification: Notification) => void; /** Called when the notification fails to display. */ onError?: (notification: Notification) => void;};Browser Support
The Notifications API is supported in all modern browsers. It is not available in iOS Safari (as of 2025) or on the server. All primitives degrade gracefully — show() returns null, close() is a no-op, and permission() returns "unknown".
Changelog
See CHANGELOG.md