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

strapi-plugin-notifier

v1.2.3

Published

A highly configurable notification inbox plugin for the Strapi v5 admin panel

Readme

strapi-plugin-notifier

A first-class notification system for Strapi v5 admin panels. Adds a live bell icon to the sidebar, a full notification inbox, a runtime Settings panel, and a simple API to send notifications from anywhere in your Strapi application.

Features

  • Live bell icon — badge count updated by polling the server (interval configurable)
  • Notification inbox — filter by type, mark as read, dismiss, load more, clear all
  • Targeting — broadcast to all admins, or target by user ID or role code
  • Batch sending — send multiple notifications in one call, settings fetched once
  • Merge — collapse repeated notifications into one item with a count badge (fully configurable)
  • User opt-out — each admin user can mute individual notification types or opt out entirely
  • Declarative rules — trigger notifications from lifecycle hooks, eventHub events, or cron schedules — no code required beyond config/plugins.ts
  • Configurable — poll interval, page size, retention policy, UI accent colours, merge rules, and cleanup schedule
  • Settings panel — runtime configuration via Settings → Notifier (persisted in Strapi plugin store)
  • Retention cron — configurable cleanup schedule (default: 3 AM daily), respects maxDays and maxPerUser limits
  • Strapi v5 only

Installation

npm install strapi-plugin-notifier
# or
yarn add strapi-plugin-notifier

Enable the plugin in config/plugins.ts:

export default {
  notifier: {
    enabled: true,
    config: {
      // all fields are optional — see Configuration below
    },
  },
};

Configuration

All configuration is optional. Built-in defaults apply unless overridden.

// config/plugins.ts
export default {
  notifier: {
    enabled: true,
    config: {
      retention: {
        maxDays: 90,          // delete notifications older than N days
        maxPerUser: 500,      // cap notifications stored per user
        cleanupCron: '0 3 * * *', // cron schedule for cleanup job (default: 3 AM daily)
      },
      delivery: {
        pollIntervalMs: 30_000, // how often the Bell polls (ms)
        pageSize: 20,           // notifications per page in inbox
      },
      merge: {
        enabled: false,          // enable notification merging
        windowMinutes: 60,       // look back this many minutes for a merge candidate
        keyFields: ['title', 'type'], // fields that must match to merge
        countBadge: true,        // show "×N" badge on merged items in the inbox
        rewriteMessage: false,   // rewrite the message to "N× title" when merging
      },
      ui: {
        theme: {
          accent: {
            info:    '#4945ff',
            success: '#5cb85c',
            warning: '#f0ad4e',
            error:   '#ee5e52',
          },
        },
      },
    },
  },
};

Settings can also be updated at runtime from the Strapi admin panel under Settings → Notifier. Runtime settings take precedence over config/plugins.ts.

Sending notifications

Use the notifier service from anywhere in your Strapi code (services, controllers, lifecycles, webhooks):

const notifier = strapi.plugin('notifier').service('notifier');

// Broadcast to all admin users
notifier.broadcast({ title: 'Maintenance scheduled', type: 'warning' });

// Send to a specific role
notifier.toRole('strapi-editor', {
  title: 'New content submitted',
  message: 'An article is waiting for review.',
  url: '/content-manager/collection-types/api::article.article',
});

// Send to a specific admin user (by user ID)
notifier.toUser(42, {
  title: 'Your export is ready',
  type: 'success',
  url: '/uploads/export-2024.csv',
});

// Generic send with full control
notifier.send({
  title: 'Hello',
  message: 'World',
  type: 'info',
  url: 'https://example.com',
  to: { role: 'strapi-super-admin' },
});

Batch sending

Send multiple notifications in one call. Settings (including merge config) are fetched once and applied to all items — more efficient than looping over send().

await notifier.sendBatch([
  { title: 'New job application', type: 'info', to: { role: 'strapi-editor' } },
  { title: 'Export ready', type: 'success', to: { userId: 42 } },
  { title: 'Low disk space', type: 'warning' }, // broadcast
]);

Opt-out and merge rules are applied per item within the batch.

Notification options

| Field | Type | Required | Default | |-----------|-----------------------------------------------|----------|-----------| | title | string | Yes | — | | message | string | No | — | | type | 'info' \| 'success' \| 'warning' \| 'error' | No | 'info' | | url | string | No | — | | to | { userId?: number; role?: string } | No | broadcast |

Notification merging

When merge.enabled is true, sending a notification whose key fields match an existing notification (created within windowMinutes) updates that notification instead of creating a new one.

Example — a form that can be submitted many times:

// config/plugins.ts
merge: { enabled: true, windowMinutes: 60, keyFields: ['title', 'type'], countBadge: true }

// In your lifecycle / service:
notifier.toRole('strapi-editor', {
  title: 'New job application',
  type: 'info',
});

After five submissions the inbox shows a single item: New job application ×5.

Merge key fields (title, type, url) — any combination. Two notifications are considered the same if all selected fields match AND the recipient matches AND the most recent one was created within the window.

Display options:

| Option | Default | Effect | |------------------|---------|--------------------------------------------------| | countBadge | true | Shows a coloured "×N" pill next to the title | | rewriteMessage | false | Replaces the message with "N× <title>" |

Both can be toggled from the Settings → Notifier → Merge panel at runtime.

User opt-out

Each admin user can control which notifications they receive. Opt-out is applied at query time so it covers broadcast and role-targeted notifications too.

Via the API

GET  /notifier/preferences/me
PUT  /notifier/preferences/me

Get current preferences:

GET /notifier/preferences/me
→ { "data": { "userId": 5, "globalOptOut": false, "mutedTypes": ["info"] } }

Mute info and success notifications:

PUT /notifier/preferences/me
Content-Type: application/json

{ "mutedTypes": ["info", "success"] }

Opt out entirely:

PUT /notifier/preferences/me
Content-Type: application/json

{ "globalOptOut": true }

Re-enable:

PUT /notifier/preferences/me
Content-Type: application/json

{ "globalOptOut": false, "mutedTypes": [] }

Preference fields

| Field | Type | Default | Description | |----------------|--------------------|---------|------------------------------------------------| | globalOptOut | boolean | false | Suppress all notifications for this user | | mutedTypes | NotificationType[] | [] | Suppress only these notification types |

Declarative rules

Instead of calling the notifier service in code, you can declare rules in config/plugins.ts. The plugin wires up the listeners at bootstrap — notifications fire automatically with no further code changes needed.

Three trigger types are supported: lifecycle, event, and cron.

on: 'lifecycle' — content-type create/update/delete

Fires when Strapi's DB lifecycle executes for a given content-type and action.

// config/plugins.ts
notifier: {
  enabled: true,
  config: {
    rules: [
      {
        on: 'lifecycle',
        model: 'api::article.article',
        action: 'afterCreate',          // afterCreate | afterUpdate | afterDelete | afterPublish | afterUnpublish
        notification: {
          title: 'New article: {{entry.title}}',
          message: 'Created by {{entry.createdBy.firstname}}',
          type: 'info',
          to: { role: 'strapi-editor' },
        },
      },
    ],
  },
},

entry in templates is the DB record produced by the lifecycle action (result for after-hooks, params.data for before-hooks).

on: 'event' — strapi.eventHub subscription

Fires on any named event published to Strapi's internal event hub (lifecycle events, media events, plugin events, or your own custom events emitted with strapi.eventHub.emit()).

{
  on: 'event',
  event: 'media.upload',              // any strapi.eventHub event name
  filter: { uid: 'api::article.article' }, // optional shallow key=value filter on payload
  notification: {
    title: 'File uploaded: {{media.name}}',
    type: 'info',
    to: 'broadcast',
  },
},

Custom events from your own services:

// In your service
strapi.eventHub.emit('order.placed', { order });

// In config/plugins.ts
{
  on: 'event',
  event: 'order.placed',
  notification: {
    title: 'New order: #{{order.id}}',
    message: '{{order.customerName}} — €{{order.total}}',
    type: 'success',
    to: { role: 'strapi-super-admin' },
  },
},

on: 'cron' — time-based notifications

Fires on a cron schedule. Standard 5-field cron expressions.

{
  on: 'cron',
  schedule: '0 9 * * 1',   // Every Monday at 9 AM
  notification: {
    title: 'Weekly reminder: review pending content',
    type: 'warning',
    to: { role: 'strapi-editor' },
  },
},

Notification templates

Every field in notification accepts either a static value or a function for dynamic content.

String interpolation — use {{path.to.field}} dot-notation to resolve values from the event context:

notification: {
  title: 'New order from {{entry.customer.name}}',
  message: 'Total: {{entry.total}} — shipped to {{entry.address.city}}',
}

Function templates — for logic that can't be expressed as a template string:

notification: {
  title: (ctx) => `${ctx.entry.priority === 'high' ? '🔴' : ''} Order #${ctx.entry.id}`,
  type: (ctx) => ctx.entry.priority === 'high' ? 'error' : 'info',
}

Targeting in rules

The to field supports the same options as the programmatic API, plus a userIdFrom path resolver:

| Value | Behaviour | |-----------------------------|---------------------------------------------------------| | 'broadcast' (or omitted) | Send to all admin users | | { role: 'strapi-editor' } | Send to all users with this role code | | { userId: 42 } | Send to a specific admin user | | { userIdFrom: 'entry.createdBy.id' } | Resolve user ID via dot-path into the event context — useful for "notify the author" patterns |

// Notify the author when their content is rejected
{
  on: 'lifecycle',
  model: 'api::article.article',
  action: 'afterUpdate',
  when: (ctx) => ctx.entry.status === 'rejected',
  notification: {
    title: 'Your article was rejected',
    to: { userIdFrom: 'entry.createdBy.id' },
  },
},

when guard

Add a when function to conditionally skip the notification. Return false to suppress it.

{
  on: 'lifecycle',
  model: 'api::article.article',
  action: 'afterUpdate',
  when: (ctx) => ctx.entry.publishedAt != null,   // only fire when published
  notification: {
    title: 'Article published: {{entry.title}}',
    type: 'success',
    to: 'broadcast',
  },
},

when is not available on cron rules (there is no event context to filter on).


API routes

All routes require admin::isAuthenticatedAdmin. The plugin mounts under /notifier/.

| Method | Path | Description | |--------|------------------------------------|----------------------------------------| | GET | /notifier/notifications | List notifications (paginated) | | PUT | /notifier/notifications/read-all | Mark all as read | | DELETE | /notifier/notifications | Clear all notifications | | PUT | /notifier/notifications/:id/read | Mark one as read | | DELETE | /notifier/notifications/:id | Clear one notification | | GET | /notifier/config | Fetch UI config (safe for frontend) | | GET | /notifier/settings | Get full settings | | PUT | /notifier/settings | Update settings | | DELETE | /notifier/settings | Reset settings to defaults | | GET | /notifier/preferences/me | Get current user's notification prefs | | PUT | /notifier/preferences/me | Update current user's notification prefs |

Query parameters for GET /notifier/notifications:

| Param | Default | Description | |------------|---------|---------------------| | page | 1 | Page number | | pageSize | 20 | Results per page |

Permissions

Two permissions are registered under the Notifier plugin section in Settings → Roles:

  • plugin::notifier.settings.read — view the settings panel
  • plugin::notifier.settings.update — save or reset settings

Content types

| UID | Collection | Description | |------------------------------------------|-----------------------------|--------------------------------------| | plugin::notifier.notification | notifier_notifications | Notification records | | plugin::notifier.notification-preference | notifier_preferences | Per-user opt-out preferences |

Both are hidden from Content Manager and Content-Type Builder by default.

License

MIT