Higher Quality, Stronger Performance, Increased Stability, Better Developer Experience, discover everything we've shipped recently!

Strapi plugin logo for Strapi Notifier

Strapi 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.

thumbnail for Strapi Notifier

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

FieldTypeRequiredDefault
titlestringYes
messagestringNo
type'info' \| 'success' \| 'warning' \| 'error'No'info'
urlstringNo
to{ userId?: number; role?: string }Nobroadcast

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:

OptionDefaultEffect
countBadgetrueShows a coloured "×N" pill next to the title
rewriteMessagefalseReplaces 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

FieldTypeDefaultDescription
globalOptOutbooleanfalseSuppress all notifications for this user
mutedTypesNotificationType[][]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:

ValueBehaviour
'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/.

MethodPathDescription
GET/notifier/notificationsList notifications (paginated)
PUT/notifier/notifications/read-allMark all as read
DELETE/notifier/notificationsClear all notifications
PUT/notifier/notifications/:id/readMark one as read
DELETE/notifier/notifications/:idClear one notification
GET/notifier/configFetch UI config (safe for frontend)
GET/notifier/settingsGet full settings
PUT/notifier/settingsUpdate settings
DELETE/notifier/settingsReset settings to defaults
GET/notifier/preferences/meGet current user's notification prefs
PUT/notifier/preferences/meUpdate current user's notification prefs

Query parameters for GET /notifier/notifications:

ParamDefaultDescription
page1Page number
pageSize20Results 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

UIDCollectionDescription
plugin::notifier.notificationnotifier_notificationsNotification records
plugin::notifier.notification-preferencenotifier_preferencesPer-user opt-out preferences

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

License

MIT

Install now

npm install strapi-plugin-notifier

STATS

1 GitHub star1306 weekly downloads

Last updated

4 days ago

Strapi Version

5.0.0 and above

Author

github profile image for datrine
datrine

Useful links

Create your own plugin

Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.