<Inbox />

Learn how to use and customize the Inbox component in your application

By default, the <Inbox /> renders a bell button, that opens a popover on click. The popover contains the notifications list and the user preferences.

Try it without keys (Keyless Mode)

Try Novu Inbox instantly - no setup required. With Keyless mode, you can integrate and test the <Inbox /> right in your app. It’s ideal for exploring features and sending test notifications.

import React from 'react';
import { Inbox } from '@novu/react';
 
export function KeylessInbox() {
 
  return (
    <Inbox />
  );
}
Ready for the Full Experience?

Keyless mode is designed for quick exploration. To save your configuration, identify users, and unlock all of Novu's powerful features, you'll need to sign up for a free account and configure your environment. Sign up on Novu Cloud

Basic Usage

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox applicationIdentifier="YOUR_APPLICATION_IDENTIFIER" subscriber="YOUR_SUBSCRIBER_ID" />
  );
}

The Inbox component uses the routerPush prop to make your notifications navigatable. We will automatically pass the redirect.url from your workflow definitions to the routerPush prop.

To make the navigation work, you will need to specify the routerPush behaviour depending on your routing library.

import { Inbox } from '@novu/nextjs';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
    />
  );
}

Snooze

The <Inbox /> component includes built-in support for snoozing notifications, which lets users delay reminders and resurface them at a more convenient time.

When a user snoozes a notification, it is temporarily removed from the active list and automatically redelivered at the specified time. This helps reduce noise while keeping important updates accessible.

Snooze is supported in client-side SDKs starting from version 3.3.0 and is only available for Cloud workspaces.

Snoozing a notification

All dates and times are interpreted in the user's local timezone. Users can snooze a notification directly from the <Inbox /> using one of the following options:

  • Preset options:
    • An hour from now
    • Tomorrow
  • Custom date and time:
    • A built-in date and time picker allows users to select a specific future time.
Render HTML in Inbox
The selected snooze time must be at least 3 minutes in the future.

Once snoozed:

  • The notification leaves the All tab and appears in the Snoozed tab.
  • It cannot be marked as read or unread or archived until it returns.

All of these behaviors are handled automatically by the <Inbox /> component.

Developers can also configure how the snooze options are presented by customizing styles, localization keys, or using snooze-related actions exposed on the notification object. For instance, each notification instance includes .snooze() and .unsnooze() methods in the client-side SDKs, allowing you to control reminders when needed.

Snoozing is available on all plans for Cloud workspaces:

  • Free – Up to 24 hours per notification
  • Trial – Up to 90 days during the 14-day trial, same as Pro
  • Pro / Team – Up to 90 days
  • Enterprise – Defaults to 90 days, extendable on request

Managing snoozed notifications

The <Inbox /> component includes a dedicated Snoozed tab that automatically displays notifications scheduled to reappear later. From this tab, users can review all snoozed notifications and take further actions as needed.

Users can unsnooze a notification at any time, which immediately returns it to the All tab. Once unsnoozed, the notification regains its standard behavior. It can be marked as read, unread, or archived like any other item.

ActionResult
Manual unsnoozeRestores the notification exactly as it was: same tab, same read/unread state. If it was read before snoozing, it re-enters the All tab still marked read.
Automatic unsnooze (timer expires)Re-delivers the notification to All as a new, unread item with an updated delivery timestamp.

This logic is built into the component and does not require any additional configuration. Notifications automatically reappear in the All tab once their snooze duration elapses.

Using custom tabs

If you're using custom tabs in the <Inbox /> component, you can configure a dedicated "Snoozed" or "Reminders" tab by adding a snoozed: true filter to your tabs configuration. This helps users navigate more easily and focus on the reminders they've scheduled.

import { Inbox } from '@novu/nextjs';
 
<Inbox
  tabs={[
    { label: 'All', filter: {} },
    { label: 'Reminders', filter: { snoozed: true } },
  ]}
/>

This customization is optional, the built-in behavior already handles snoozed notifications. However, adding an explicit tab can help reinforce the distinction between active and pending reminders in your UI.

Event Handling

Notification Click

You can handle notification clicks without navigation by using the onNotificationClick prop. This is useful for opening modals or drawers instead of navigating to a page.

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      onNotificationClick={(notification) => {
        // your logic to handle notification click
      }}
    />
  );
}

Action Clicks

Handle action button clicks without navigation using the onPrimaryActionClick and onSecondaryActionClick props:

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      onPrimaryActionClick={(notification) => {
        // your logic to handle primary action click
      }}
      onSecondaryActionClick={(notification) => {
        // your logic to handle secondary action click
      }}
    />
  );
}

Customization

Controlled Popover State

import { Inbox } from '@novu/react';
import { useState } from 'react';
 
function Novu() {
  const [open, setOpen] = useState(false);
 
  return (
    <>
      <Inbox
        applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
        subscriber="YOUR_SUBSCRIBER_ID"
        open={open}
      />
      <button onClick={() => setOpen(!open)}>Toggle Inbox</button>
    </>
  );
}

Custom Notification Item

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      renderNotification={(notification) => (
        <div>
          <h3>{notification.subject}</h3>
          <p>{notification.body}</p>
        </div>
      )}
    />
  );
}

Custom Notification Subject

Render a custom subject for the notification, while keeping the default notification component.

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      renderSubject={(notification) => (
        <div>
          <p>{notification.subject}</p>
        </div>
      )}
    />
  );
}

Custom Notification Body

Render a custom body for the notification, while keeping the default notification component.

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      renderBody={(notification) => (
        <div>
          <p>{notification.body}</p>
        </div>
      )}
    />
  );
}

Custom notification based on condition

The <Inbox /> component supports conditional rendering of notifications using the renderNotification prop. This function receives a notification object and returns a custom React element.

You can use workflow tags, workflow identifiers, or values from the data object to customize how each notification is displayed. You can also combine multiple conditions to create dynamic and tailored notification displays.

Workflow tags condition

You can conditionally render customized notification based on workflow tags. These tags are defined in your workflow and are accessible via notification.tags property.

import { Inbox } from '@novu/react';
 
export default function CustomInbox() {
  
  return (
    <Inbox
      applicationIdentifier=""
      subscriber=""
      renderNotification={(notification) => {
        // filter based on tags
        if (notification.tags?.includes('custom')) {
          return (
            <div>
              <h3>Custom Notification Subject</h3>
              <p>Custom Notification Body</p>
            </div>
          );
        }
        // default
        return (
          <div>
            <h3>{notification.subject}</h3>
            <p>{notification.body}</p>
          </div>
        );
      }}
    />
  );
}

Workflow identifier or name condition

Each workflow has a name and a unique identifier, accessible through notification.workflow.name and notification.workflow.identifier, respectively. Either property can be used to conditionally render notifications.

import { Inbox } from '@novu/react';
export default function CutomInbox() {
  
  return (
    <Inbox
      applicationIdentifier=""
      subscriber=""
      renderNotification={(notification) => {
        // filter based on workflow identifier
        if (notification.workflow?.identifier === 'demo') {
          return (
            <div style={{ backgroundColor: 'blue' }}>
              <h3>{notification.subject}</h3>
              <p>{notification.body}</p>
            </div>
          );
        }
        // filter based on workflow name
        if (notification.workflow?.name === 'Custom') {
          return (
            <div style={{ backgroundColor: 'blue' }}>
              <h3>{notification.subject}</h3>
              <p>{notification.body}</p>
            </div>
          );
        }
        // default
        return (
          <div>
            <h3>{notification.subject}</h3>
            <p>{notification.body}</p>
          </div>
        );
      }}
    />
  );
}

Data object condition

You can also use values from the notification data object to render notifications conditionally. These values are accessible via notification.data and typically contain custom payload fields

import { Inbox } from '@novu/react';
export default function CutomInbox() {
  
  return (
    <Inbox
      applicationIdentifier=""
      subscriber=""
      renderNotification={(notification) => {
       // filter based on data object key
       if (notification.data?.priority) {
        return (
          <div style={{ backgroundColor: 'red' }}>
            <h3>{notification.subject}</h3>
            <p>{notification.body}</p>
          </div>
        );
      }
        // default
        return (
          <div>
            <h3>{notification.subject}</h3>
            <p>{notification.body}</p>
          </div>
        );
      }}
    />
  );
}

Rendering HTML

To render HTML tags in inbox, user renderBody props and render the notification body as a dangerouslySetInnerHTML element.

import { Inbox } from '@novu/react';
 
function NovuInbox() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      renderBody={(notification) => (
        <div>
          <p dangerouslySetInnerHTML={{ __html: notification.body }} />
        </div>
      )}
    />
  );
}

Steps:

  1. Create a workflow with in-app step.
  2. Toggle on the Disable content sanitization option on the top right corner of the in-app step.
  3. Use html tags in in-app step body as shown in the image below
Render HTML in Inbox

HTML Content:

{{subscriber.firstName}}, A <b>Good news!</b> We've just launched the <i>advanced 
analytics dashboard</i> you asked for. Check it out <a href="{{payload.analyticsPage}}" 
target="_blank">here</a> and gain deeper insights into your usage.

Custom Bell

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      renderBell={(unreadCount) => (
        <div>
          <span>{unreadCount}</span>
        </div>
      )}
    />
  );
}

Filtering Preferences

Customize visible preferences using the preferencesFilter prop to display only relevant preferences to your users. The filtering works by matching workflow tags with the specified filter tags value.

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriber="YOUR_SUBSCRIBER_ID"
      preferencesFilter={{ tags: ['general', 'admin', 'security'] }}
    />
  );
}

Subscriber Data Upsert

The <Inbox /> component supports real-time subscriber data updates when properly configured with HMAC authentication. This allows you to update subscriber information directly through the component without making separate API calls:

import { Inbox } from '@novu/react';
 
function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriberHash="GENERATED_HMAC_HASH" // Generated on your backend
      subscriber={{
        subscriberId: "YOUR_SUBSCRIBER_ID",
        firstName: "John",
        lastName: "Doe",
        email: "john@example.com"
      }}
    />
  );
}

To enable real-time subscriber data updates, make sure to provide the subscriberHash for secure HMAC authentication. This allows you to update subscriber data directly through the component while maintaining security.

Learn more about setting up HMAC authentication in the Enabling HMAC Encryption guide.

Data object

The data object is a key-value store within each notification, used to extend <Inbox /> notifications by embedding step-specific metadata. It provides flexible notification handling, supporting both static and dynamic values:

  • Static Values: These are hardcoded into the notification step—for example, a string like "status": "merged" or "icon": "heart". These values don't change based on the recipient or context.
  • Dynamic Values: These values are derived from subscriber or payload data. For instance, they can reference subscriber.firstName or payload.issueId to tailor notifications for individual users.

You can pass data such as:

  • reactionType: To display specific icons such as 👍, ❤️, or a comment bubble.
  • entityId (like pullRequestId or issueId): Create direct links or show relevant badges (e.g., a GitHub logo linking to the PR).
  • status or actionType: To show visual cues such as colored dots or status icons (e.g., green for 'merged', orange for 'commented').
Data object for in-app Inbox step

The data object is defined within a workflow's in-app step in the Novu dashboard. Each key-value pair, referred to as a property, can be static or dynamic, and you get up to 10 properties per in-app step. These properties are accessible on the frontend via the notification.data property.

import { Inbox } from '@novu/react';
 
<Inbox
  applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
  subscriber="YOUR_SUBSCRIBER_ID"
  renderNotification={(notification) => (
    <div>
      <p>{notification.data?.customKey}</p>
      <p>{notification.data?.dataId}</p>
    </div>
  )}
/>

notification.data is included in the client response, so do not store any sensitive data in it.

By default, notification.data is untyped. To ensure type safety, declare the NotificationData interface globally.

declare global {
  interface NotificationData {
    customKey?: string;
    dataId?: number;
  }
}

This lets TypeScript infer the structure of notification.data, preventing errors when accessing properties. However, as not all notifications include the same keys, check properties for existence before usage.