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

payload-notifications

v0.1.10

Published

Generic notification infrastructure for Payload CMS with email, in-app, and external callback channels.

Readme

payload-notifications

Multi-channel notification infrastructure for Payload CMS with document subscriptions and live messages.

npm version License: MIT

Overview

Payload has no built-in way to notify users about events. This plugin provides a server-side notify() / subscribe() / getSubscribers() API that you wire into your own hooks, endpoints, and workflows. A single notify() call can store an in-app notification, send an email, and fire an external callback -- each channel independent and optional. Everything is user-scoped with strict access control.

Features

  • Multi-channel delivery -- in-app, email, and external callbacks from a single notify() call. Per-user preferences control which channels are active.
  • Document subscriptions -- users follow collections or globals. getSubscribers() returns follower IDs for fan-out. Supports auto-subscribe (e.g. on first comment) and manual follows.
  • Live messages -- template tokens (t.actor, t.document('title'), t.meta('key')) that resolve against fresh data at read time. Renamed users and updated titles are reflected in existing notifications.
  • External callback -- onNotify hook for pushing to Slack, webhooks, queues, or anything else.
  • Admin bell component -- optional popover UI with unread badge, mark-read, unsubscribe, and delete actions.

Installation

pnpm add payload-notifications

Usage

// payload.config.ts
import { buildConfig } from "payload";
import { notificationsPlugin } from "payload-notifications";

export default buildConfig({
  // ...
  plugins: [
    notificationsPlugin({
      email: {
        generateSubject: ({ notification }) =>
          `Notification: ${notification.event}`,
        generateHTML: ({ notification, recipient }) =>
          `<p>Hi ${recipient.displayName}, ${notification.message}</p>`,
      },
    }),
  ],
});

Sending notifications

import { notify } from "payload-notifications";

await notify(req, {
  recipient: user.id,
  event: "comment.created",
  actor: commenter.id,
  message: "Someone replied to your post",
  documentReference: { entity: "collection", slug: "posts", id: post.id },
});

Subscriptions and fan-out

import {
  subscribe,
  getSubscribers,
  notify,
  createLiveMessage,
} from "payload-notifications";

const docRef = { entity: "collection", slug: "posts", id: post.id } as const;

await subscribe(req, { userId: commenter.id, documentReference: docRef });

const subscribers = await getSubscribers(req, docRef);
for (const recipientId of subscribers) {
  await notify(req, {
    recipient: recipientId,
    event: "comment.created",
    actor: commenter.id,
    message: createLiveMessage(
      (t) => t`${t.actor} commented on "${t.document("title")}"`,
    ),
    documentReference: docRef,
  });
}

Live messages

Static strings go stale when users rename themselves or documents get updated. Live messages store tokens that resolve against fresh data at read time:

import { notify, createLiveMessage } from "payload-notifications";

await notify(req, {
  recipient: user.id,
  event: "post.updated",
  actor: editor.id,
  message: createLiveMessage(
    (t) => t`${t.actor} edited "${t.document("title")}"`,
  ),
  documentReference: { entity: "collection", slug: "posts", id: post.id },
});
  • t.actor -- actor's display name (from admin.useAsTitle on the user collection)
  • t.document(field) -- a field from the referenced document
  • t.meta(key) -- a key from the notification's meta object

Options

| Option | Type | Default | Description | | ------------------- | ------------------------- | ----------------- | ------------------------------------------------------------------- | | email | NotificationEmailConfig | -- | generateSubject and generateHTML functions. Omit to skip email. | | onNotify | NotifactionCallback | -- | Callback for every notification (Slack, webhooks, queues, etc). | | notificationsSlug | string | "notifications" | Slug for the notifications collection. | | subscriptionsSlug | string | "subscriptions" | Slug for the subscriptions collection. | | pollInterval | number | 30 | Poll interval in seconds for the admin bell component. |

API

All functions are server-side and require a PayloadRequest:

| Export | Description | | ------------------------------- | -------------------------------------------------------------- | | notify(req, input) | Deliver a notification. Respects per-user channel preferences. | | subscribe(req, opts) | Subscribe a user to a document. Deduplicates automatically. | | unsubscribe(req, userId, ref) | Remove a subscription. | | getSubscribers(req, ref) | Return all user IDs subscribed to a document. | | createLiveMessage(fn) | Build a serializable message template with dynamic tokens. |

Contributing

This plugin lives in the payload-plugins monorepo.

Development

pnpm install

# watch this plugin for changes
pnpm --filter payload-notifications dev

# run the Payload dev app (in a second terminal)
pnpm --filter sandbox dev

The sandbox/ directory is a Next.js + Payload app that imports plugins via workspace:* -- use it to test changes locally.

Code quality

  • Formatting & linting -- handled by Biome, enforced on commit via husky + lint-staged.
  • Commits -- must follow Conventional Commits with a valid scope (e.g. fix(payload-notifications): ...).
  • Changesets -- please include a changeset in your PR by running pnpm release.

Issues & PRs

Bug reports and feature requests are welcome -- open an issue.

License

MIT