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

@cloudsignal/notifications

v0.1.2

Published

Realtime in-app notifications for React, powered by CloudSignal MQTT

Downloads

15

Readme

@cloudsignal/notifications

Realtime in-app notifications for React — powered by CloudSignal MQTT.

Drop-in components for notification bell, toast popups, notification feed, and channel subscriptions with hooks for fine-grained control.

Install

npm install @cloudsignal/notifications @cloudsignal/mqtt-client

Quick Start

import {
  NotificationProvider,
  NotificationBell,
  NotificationToast,
} from "@cloudsignal/notifications";

export default function App() {
  return (
    <NotificationProvider
      userId="user-123"
      connection={{
        host: "wss://connect.cloudsignal.app:18885/",
        username: "alice@org_k7xm4pqr2n5t",
        password: "alice-password",
      }}
      channels={["announcements"]}
    >
      <NotificationBell />
      <NotificationToast />
      <YourApp />
    </NotificationProvider>
  );
}

Provider

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | userId | string | Yes | — | Unique user identifier for inbox subscriptions | | connection | NotificationConnectionConfig | Yes | — | MQTT connection config (see below) | | channels | string[] | No | [] | Channel IDs to auto-subscribe on mount | | maxNotifications | number | No | 50 | Max notifications kept in memory | | onNotification | (n: Notification) => void | No | — | Callback fired on each incoming notification | | debug | boolean | No | false | Enable debug logging to console |

Connection Options

Direct Credentials

<NotificationProvider
  userId="user-123"
  connection={{
    host: "wss://connect.cloudsignal.app:18885/",
    username: "alice@org_k7xm4pqr2n5t",
    password: "alice-password",
  }}
>

Token Auth (Secret Key)

<NotificationProvider
  userId="user-123"
  connection={{
    host: "wss://connect.cloudsignal.app:18885/",
    organizationId: "org-uuid",
    secretKey: "sk_...",
  }}
>

External IdP (Supabase, Clerk, Firebase, Auth0)

import { useSession } from "@supabase/auth-helpers-react";

function App() {
  const session = useSession();

  return (
    <NotificationProvider
      userId={session.user.id}
      connection={{
        host: "wss://connect.cloudsignal.app:18885/",
        organizationId: "org-uuid",
        externalToken: session.access_token,
        tokenServiceUrl: "https://auth.cloudsignal.app",
      }}
    >
      <YourApp />
    </NotificationProvider>
  );
}

Hooks

All hooks must be called inside a <NotificationProvider>.

| Hook | Returns | Description | |------|---------|-------------| | useNotifications() | { notifications, markAsRead, markAllAsRead, dismiss, clearAll } | Full notification feed and actions | | useUnreadCount() | { count, reset } | Lightweight unread badge counter | | useChannels() | { channels, subscribe, unsubscribe } | Runtime channel subscription management | | useNotificationEvent(type, cb) | void | Filtered listener for a specific notification type |

useNotifications

const { notifications, markAsRead, markAllAsRead, dismiss, clearAll } = useNotifications();

// notifications: InternalNotification[] (newest first)
// markAsRead(id: string): void
// markAllAsRead(): void
// dismiss(id: string): void — remove from list
// clearAll(): void — clear all notifications

useUnreadCount

const { count, reset } = useUnreadCount();

// count: number — current unread count
// reset(): void — marks all as read

useChannels

const { channels, subscribe, unsubscribe } = useChannels();

// channels: string[] — currently subscribed channel IDs
// subscribe(channelId: string): void
// unsubscribe(channelId: string): void

useNotificationEvent

useNotificationEvent("order.shipped", (notification) => {
  // Fires only for notifications with type === "order.shipped"
  console.log("Order shipped:", notification.data);
});

Components

NotificationBell

Bell icon with unread badge and dropdown list.

| Prop | Type | Default | Description | |------|------|---------|-------------| | maxVisible | number | 5 | Max notifications in dropdown | | onNotificationClick | (n: InternalNotification) => void | — | Click handler for a notification | | renderEmpty | () => ReactNode | — | Custom empty state | | className | string | — | CSS class for outer container |

<NotificationBell maxVisible={10} onNotificationClick={(n) => router.push(n.action?.url)} />

NotificationToast

Auto-renders incoming notifications as temporary toasts.

| Prop | Type | Default | Description | |------|------|---------|-------------| | position | "top-right" \| "top-left" \| "bottom-right" \| "bottom-left" | "top-right" | Toast position | | duration | number | 5000 | Auto-dismiss duration in ms | | maxVisible | number | 3 | Max simultaneous toasts | | onClick | (n: Notification) => void | — | Click handler | | renderToast | (n: Notification) => ReactNode | — | Custom toast renderer |

<NotificationToast position="bottom-right" duration={8000} />

NotificationList

Scrollable notification feed.

| Prop | Type | Default | Description | |------|------|---------|-------------| | onNotificationClick | (n: InternalNotification) => void | — | Click handler | | renderItem | (n: InternalNotification) => ReactNode | — | Custom item renderer | | renderEmpty | () => ReactNode | — | Custom empty state | | className | string | — | CSS class |

<NotificationList
  onNotificationClick={(n) => router.push(n.action?.url)}
  renderEmpty={() => <p>No notifications yet</p>}
/>

NotificationItem

Single notification card. Used internally by NotificationBell and NotificationList.

| Prop | Type | Required | Description | |------|------|----------|-------------| | notification | InternalNotification | Yes | The notification to render | | onClick | () => void | No | Click handler | | onDismiss | () => void | No | Dismiss handler |

MQTT Topics

All notifications flow over MQTT topics under $notifications/:

| Topic | Purpose | |-------|---------| | $notifications/{userId}/inbox | Personal notifications | | $notifications/{userId}/inbox/{category} | Categorized personal notifications | | $notifications/channels/{channelId} | Channel broadcasts |

The provider automatically subscribes to $notifications/{userId}/inbox/# (wildcard covers base + categories) and each channel topic specified in the channels prop.

Types

Notification

interface Notification {
  id: string;           // Unique notification ID
  type: string;         // e.g. "order.shipped", "payment.received"
  title: string;        // Short title
  body: string;         // Body text
  icon?: string;        // Icon URL or emoji
  image?: string;       // Rich image URL
  action?: {            // Call-to-action
    label: string;
    url: string;
  };
  category?: string;    // For filtering (e.g. "orders", "payments")
  channel?: string;     // Channel ID for broadcasts
  sender?: {            // Who triggered this
    name: string;
    avatar?: string;
  };
  ts: number;           // Timestamp in milliseconds
  data?: Record<string, unknown>; // Arbitrary metadata
}

InternalNotification

Extends Notification with client-side state:

interface InternalNotification extends Notification {
  isRead: boolean;     // Whether the user has read this
  receivedAt: number;  // When the client received it
}

Architecture

@cloudsignal/notifications (this package)
  └── @cloudsignal/mqtt-client (peer dependency — MQTT transport)
        └── VerneMQ broker (CloudSignal infrastructure)
  • Receive-only — the SDK never publishes to MQTT. Notifications are sent server-side via the CloudSignal API or by publishing directly with a secret key.
  • 1Hz state sync — notification state is stored in refs and synced to React state at 1Hz for batched re-renders.
  • QoS 1 — all subscriptions use QoS 1 for reliable delivery.

License

MIT