npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@devkit-labs/notifier

v0.1.6

Published

Lightweight browser notification utility with permission handling, clean API, fallbacks, and deduplication out of the box

Readme

@devkit-labs/notifier - Beta

A lightweight, framework-agnostic browser notification utility with automatic permission handling, typed notification methods, de-duplication and smart fallbacks.

📋 Live Demo | 📦 NPM Package

Use Case

  • To notify the user about a time-consuming server or client-side task, such as image generation/editing or video generation/editing
  • Long-running file uploads or downloads, where the user might leave the tab until completion
  • Polling operations that require several minutes to finish
  • Any workflow where the user starts a process and may switch tabs, and you want to notify them or bring back their attention when the task completes or fails

Features

  • Typed Notification Methods - Pre-configured methods for success, error, info, warning, and general messages
  • Simple Configuration - Configure once with notifier.init(), use everywhere with notify.*
  • Automatic Permission Handling - Requests notification permission automatically when initialized, handles permission rejection by showing an alert with a configurable message
  • Framework Agnostic - Works with any JavaScript framework or vanilla JS
  • Full Notification API Support - Supports all native Notification API options
  • Deduplication - By default only shows notifications when user is on other tabs, can be changed
  • Smart Tag Management - Automatically generates unique tags to prevent notification collisions
  • Auto Favicon Detection - Uses the page's favicon as notification icon when none provided
  • Smart Fallbacks - Plays beep sound on source tab when notifications are blocked
  • TypeScript Support - Fully typed with comprehensive interfaces
  • Lightweight - Minimal dependencies and small bundle size
  • Browser Compatibility - Works across all modern browsers

Installation

npm install @devkit-labs/notifier

Bare Minimum Setup

import { notifier, notify } from "@devkit-labs/notifier";

// Initialize once at app startup (important!)
notifier.init({
   onPermissionDeny: () => {
      // show a toast or modal to ask for permission
   },
});

// Use anywhere in your app after initialization
notify.success("Hello World!");

That's it! The library will:

  • ✅ Automatically request notification permission
  • ✅ Use default icons and settings
  • ✅ Fall back to audio beep if notifications are blocked/denied
  • ✅ Only show notifications when user is on other tabs (by default)

Quick Start

// Simple import - everything you need
import { notifier, notify } from "@devkit-labs/notifier";

// Full imports including utilities
import {
   notifier,
   notify,
   getPermissionStatus,
   isNotificationSupported,
} from "@devkit-labs/notifier";

// Configure the notifier with optional custom icons and settings,
// Note : All fields are optional, Notifier comes with default icons and settings
const notifierConfig = {
   alertSound: "/alert-sound.mp3",
   showOnSourceTab: false, // false by default
   onPermissionDeny: () => {
      console.log("Notification permission denied");
      // Handle permission denial (e.g., show a toast message)
   },
   icons: {
      success: "/success-icon.png",
      error: "/error-icon.png",
      info: "/info-icon.png",
      warning: "/warning-icon.png",
   },
};

// Initialize with config and request notification permission
notifier.init(notifierConfig); // can be initialized without config too
// notifier.init(); uses default icons & settings

// Use typed notification methods
notify.success("Task completed!");
notify.error("Upload failed!", {
   body: "Please check your connection and try again.",
});
notify.warning("Storage almost full", {
   body: "Please free up some space.",
});
notify.info("New feature available", {
   body: "Check out our latest update.",
});
notify.message("Meeting reminder", {
   body: "Team standup starts in 5 minutes.",
}); // uses favicon

⚠️ Important: Initialize Once

Always initialize the notifier only once in your application's entry point (e.g., main.js, index.js, app.js, or close to your root component).

// Initialize once in your main entry file
// main.js or index.js
import { notifier } from "@devkit-labs/notifier";

notifier.init({
   icons: {
      success: "/icons/success.png",
      error: "/icons/error.png",
   },
});

// Then use notify.* anywhere in your app after initialization
notify.success("Your task is complete");
// Multiple initializations will overwrite each other
// file1.js
notifier.init({ alertSound: "/sound1.mp3" });

// file2.js
notifier.init({ alertSound: "/sound2.mp3" }); // Overwrites file1's config

API Reference

Configuration

notifier.init(config?)

Initialize the notifier with optional configuration. This should be called once at the start of your application.

interface NotifierConfig {
   alertSound?: string; // Custom alert sound URL for audio fallback (optional)
   showOnSourceTab?: boolean; // (default : false) Whether to show the notifications when user is on source tab, recommended way would be to handle it using application UI(toast, modal etc)
   onPermissionDeny?: () => void; // Callback function called when permission is denied
   icons?: {
      // icons path for various type of notifications, if not provided uses default icons for each
      success?: string;
      error?: string;
      info?: string;
      warning?: string;
   };
}

// Example configuration
notifier.init({
   alertSound: "/sounds/notification.mp3",
   onPermissionDeny: () => {
      // Handle permission denial
      console.log("User denied notification permission");
   },
   icons: {
      success: "data:image/svg+xml,<svg>...</svg>",
      error: "/icons/error.png",
      info: "/icons/info.png",
      warning: "/icons/warning.png",
   },
});

Typed Notification Methods

All notification methods follow the same pattern: notify.{type}(title, options?)

notify.success(title, options?)

Shows a success notification with the configured success icon.

notify.success("Upload Complete!");
notify.success("File Saved", {
   body: "Your document has been saved successfully.",
   requireInteraction: true,
   onclick: () => window.focus(),
});

notify.error(title, options?)

Shows an error notification with the configured error icon.

notify.error("Upload Failed!");
notify.error("Connection Error", {
   body: "Unable to connect to server.",
   requireInteraction: true,
});

notify.info(title, options?)

Shows an informational notification with the configured info icon.

notify.info("New Feature");
notify.info("Update Available", {
   body: "Version 2.0 is now available.",
   onclick: () => window.open("/updates"),
});

notify.warning(title, options?)

Shows a warning notification with the configured warning icon.

notify.warning("Low Storage");
notify.warning("Unsaved Changes", {
   body: "You have unsaved changes that will be lost.",
   requireInteraction: true,
});

notify.message(title, options?)

Shows a general message notification using the page's favicon (no configured icon).

notify.message("New Message");
notify.message("Meeting Reminder", {
   body: "Daily standup starts soon.",
});

NotificationOptions

All notification methods accept the same options object, which are same as accepted by native Notification api:

interface NotificationOptions {
   badge?: string; // URL of badge image
   body?: string; // Notification body text
   data?: any; // Custom data to attach
   dir?: "auto" | "ltr" | "rtl"; // Text direction
   image?: string; // URL of notification image
   lang?: string; // Language code
   onclick?: (event: Event) => void; // Click handler
   onclose?: (event: Event) => void; // Close handler
   onerror?: (event: Event) => void; // Error handler
   onshow?: (event: Event) => void; // Show handler
   renotify?: boolean; // Whether to replace previous notifications
   requireInteraction?: boolean; // Keep notification visible until user interacts
   silent?: boolean; // Whether notification should be silent
   timestamp?: number; // Custom timestamp
   title?: string; // Alternative title (use parameter instead)
   vibrate?: number[]; // Vibration pattern for mobile devices
}

Utility Functions

getPermissionStatus()

Returns the current notification permission status.

import { getPermissionStatus } from "@devkit-labs/notifier";

const status = getPermissionStatus(); // 'default' | 'granted' | 'denied'

isNotificationSupported()

Checks if the browser supports notifications.

import { isNotificationSupported } from "@devkit-labs/notifier";

if (isNotificationSupported()) {
   // Notifications are supported
}

Examples

Basic Usage

import { notifier, notify } from "@devkit-labs/notifier";

// Initialize once
notifier.init({
   onPermissionDeny: () => {
      // Show app-specific message when permission is denied
      showToast(
         "Notifications are disabled. Enable them for better experience."
      );
   },
   icons: {
      success: "/icons/success.svg",
      error: "/icons/error.svg",
   },
});

// Use anywhere
notify.success("Task completed!");
notify.error("Something went wrong!");

Rich Notifications

notify.error("Upload Failed", {
   body: "The file could not be uploaded to the server.",
   badge: "/icons/badge.png",
   image: "/images/error-preview.jpg",
   requireInteraction: true,
   vibrate: [200, 100, 200],
   data: {
      fileId: "12345",
      fileName: "document.pdf",
   },
   onclick: () => {
      console.log("User clicked on error notification");
      window.focus();
   },
   onclose: () => {
      console.log("Error notification was closed");
   },
});

Tab Visibility Control

// By default it will only show notification if user is NOT on the current tab
notify.success("Background Task Complete", {
   body: "Your export has finished.",
   onclick: () => {
      window.focus();
      // Navigate to results page
   },
});

// Always show notification
notify.warning("Important Alert", {
   body: "This will show regardless of tab visibility.",
   showOnSourceTab: true, // Override the default (false)
});

Permission Denied Fallbacks

When notification permission is denied, the library automatically:

  1. Triggers the onPermissionDeny callback (if configured)
  2. Plays a beep sound on source tab when a notification is triggered (unless silent: true).
// Configure permission denial handler
notifier.init({
   onPermissionDeny: () => {
      // Show your app's notification (toast, modal, etc.)
      showToast("Notifications disabled. Enable for alerts!");
   },
   alertSound: "/sounds/beep.mp3", // Custom beep sound (default : system sound)
});

// This will trigger onPermissionDeny callback if permission is denied
notify.error("Critical Error", {
   body: "This is important information for the user.",
});

// Silent fallback (no beep sound)
notify.info("Silent Update", {
   body: "This won't make a sound even in fallback mode.",
   silent: true, // No beep sound when permission denied
});

Browser Support

  • Chrome/Edge: Full support
  • Firefox: Full support
  • Safari: Full support (with some limitations on mobile)
  • Mobile browsers: Support varies by platform

Best Practices

  1. Initialize once, use everywhere - Call notifier.init() only once in your main entry file (index.js, main.js, app.js). Multiple initializations will overwrite previous configurations.
  2. Handle denied permissions - Configure onPermissionDeny callback to handle permission rejection gracefully with your app's UI (toast, modal, etc.)
  3. Use appropriate notification types - Use success for completions, error for failures, etc.
  4. Don't spam notifications - Only show notifications for important events that users care about
  5. Provide meaningful content - Use clear titles and descriptive body text
  6. Handle clicks appropriately - Focus your app window or navigate to relevant content when notifications are clicked
  7. Respect user preferences - Check permission status and handle denied permissions gracefully
  8. Use showOnSourceTab wisely - Consider whether users need to see notifications when they're already using your app. In such cases, show notifications using your application UI instead
  9. Test fallback behavior - Ensure your app works well even when notifications are blocked (audio beep + callback)

TypeScript

This package is written in TypeScript and includes full type definitions. No additional @types packages are needed.

// All methods and options are fully typed
import {
   notifier,
   notify,
   NotifierConfig,
   NotificationOptions,
} from "@devkit-labs/notifier";

const config: NotifierConfig = {
   onPermissionDeny: () => console.log("Permission denied"),
   icons: {
      success: "/success.png",
   },
};

const options: NotificationOptions = {
   requireInteraction: true,
};

notify.success("Typed!", options);

License

MIT

Contributing

We welcome contributions! If you'd like to help improve this package, please submit a pull request via our GitHub repository.