📜 Description
When using @novu/js with useCache: true, the in-memory notifications cache does not dedupe entries by notification.id on insert. If the same notification payload is written into the cache more than once: for example, when multiple Novu client instances (multiple browser tabs, multiple React roots, etc.) receive the same new notification socket event - the cache returns the same notification multiple times from getAll(), and @novu/react's useNotifications() hook consequently hands back duplicate entries to consumers.
The duplicate entries disappear on a full refresh (which replays from the server-side /notifications HTTP response) but reappear the next time a notification arrives while multiple subscribers are active. The count of duplicates scales linearly with the number of mounted Novu clients (3 tabs -> 3 copies of each newly received notification).
👟 Reproduction steps
Minimum reproducible example with @novu/js directly:
const { Novu } = require('@novu/js');
const novu = new Novu({
applicationIdentifier: 'app',
subscriber: 'sub',
useCache: true,
});
const args = { archived: false, limit: 10 };
const notification = {
id: 'n1',
transactionId: 't1',
createdAt: new Date().toISOString(),
isRead: false,
isSeen: false,
isArchived: false,
isSnoozed: false,
channelType: 'in_app',
to: { subscriberId: 'sub' },
body: 'body',
subject: 'subject',
tags: [],
data: {},
};
novu.notifications.cache.unshift(args, notification);
novu.notifications.cache.unshift(args, notification);
console.log(novu.notifications.cache.getAll(args).notifications.map(n => n.id));
End-to-end reproduction using @novu/react:
- In a React app that mounts
<NovuProvider> and calls useNotifications({ archived: false, limit: 10 }), open the app in 2+ browser tabs so multiple Novu clients are alive for the same subscriber.
- Trigger a new in-app notification for that subscriber (via the Novu dashboard or the trigger API).
- Observe the rendered inbox in any tab: the new notification appears N times, where N is the number of currently mounted Novu clients.
- Refresh the page, the inbox re-renders from the HTTP response and the duplicates are gone… until the next notification arrives.
- React also emits the "Encountered two children with the same key" warning, because consumers naturally use notification.id as the list key
👍 Expected behavior
notifications.cache.unshift() should be idempotent with respect to notification.id within a given query scope. Calling it twice with the same notification should leave the cache holding exactly one entry with that id. As a consequence, cache.getAll() and useNotifications() should never return two items with the same id. A safe default would be to replace or skip any entry whose id already exists in the cached list before prepending.
👎 Actual Behavior with Screenshots
notifications.cache.unshift() should be idempotent with respect to notification.id within a given query scope
- Calling it twice with the same notification should leave the cache holding exactly one entry with that id
- As a consequence,
cache.getAll() and useNotifications() should never return two items with the same id
- A safe default would be to replace or skip any entry whose id already exists in the cached list before prepending
Novu version
3.14.2
npm version
10.9.4
node version
22.21.0
📃 Provide any additional context for the Bug.
No response
👀 Have you spent some time to check if this bug has been raised before?
🏢 Have you read the Contributing Guidelines?
Are you willing to submit PR?
None
📜 Description
When using @novu/js with useCache: true, the in-memory notifications cache does not dedupe entries by notification.id on insert. If the same notification payload is written into the cache more than once: for example, when multiple Novu client instances (multiple browser tabs, multiple React roots, etc.) receive the same new notification socket event - the cache returns the same notification multiple times from getAll(), and
@novu/react'suseNotifications()hook consequently hands back duplicate entries to consumers.The duplicate entries disappear on a full refresh (which replays from the server-side
/notificationsHTTP response) but reappear the next time a notification arrives while multiple subscribers are active. The count of duplicates scales linearly with the number of mounted Novu clients (3 tabs -> 3 copies of each newly received notification).👟 Reproduction steps
Minimum reproducible example with @novu/js directly:
End-to-end reproduction using
@novu/react:<NovuProvider>and callsuseNotifications({ archived: false, limit: 10 }), open the app in 2+ browser tabs so multiple Novu clients are alive for the same subscriber.👍 Expected behavior
notifications.cache.unshift()should be idempotent with respect tonotification.idwithin a given query scope. Calling it twice with the same notification should leave the cache holding exactly one entry with that id. As a consequence,cache.getAll()anduseNotifications()should never return two items with the same id. A safe default would be to replace or skip any entry whose id already exists in the cached list before prepending.👎 Actual Behavior with Screenshots
notifications.cache.unshift()should be idempotent with respect tonotification.idwithin a given query scopecache.getAll()anduseNotifications()should never return two items with the same idNovu version
3.14.2
npm version
10.9.4
node version
22.21.0
📃 Provide any additional context for the Bug.
No response
👀 Have you spent some time to check if this bug has been raised before?
🏢 Have you read the Contributing Guidelines?
Are you willing to submit PR?
None