The @novu/react package introduces a more flexible and customizable way to display notifications in your application. This guide outlines the key differences between the @novu/notification-center package and the new @novu/react package. Follow the steps below to migrate your application to the latest Inbox version.

Why you should upgrade to @novu/react

  • Customization: The @novu/react package provides more customization options for the appearance and behavior of the notification components.
  • Flexibility: The new package offers more flexibility in handling notifications and integrating with third-party libraries.
  • Performance: It is optimized for performance and provides a smoother user experience.
  • Bundle Size: The new package has a smaller bundle size and improved tree-shaking capabilities.
  • Compatibility with the @novu/framework: The @novu/react package is designed to work seamlessly with the @novu/framework package for creating and managing notifications.

Breaking Changes

The @novu/react package introduces several breaking changes compared to the @novu/notification-center package. Here are the key differences:

Components

  • The PopoverNotificationCenter component has been replaced with the Inbox component.
  • The NotificationCenter component has been replaced with the Notifications component.
  • The NotificationBell component has been replaced with the Bell component.

Styling

  • The styles props is replaced by an enchanced and easy to use appearance prop to customize the appearance of the notification components. For more information on appearance customization visit here.

Notification

  • Removal of seen , lastSeenDate, content, templateIdentifier, payload, cta properties from the Notification object.
  • We have introduced archive functionality to the Notification object.
type Notification = {
- _id: string;
+ id: string;

- content: string;
+ body: string;

- cta: IMessageCTA;
+ redirect?: Redirect;
+ primaryAction?: Action;
+ secondaryAction?: Action;

- channel: ChannelTypeEnum;
+ channelType: ChannelType;

- payload: Record<string, unknown>;
+ data?: Record<string, unknown>;

- subscriber: Subscriber;
+ to: Subscriber;

- seen: boolean;
- lastSeenDate: string;
- templateIdentifier: string;

+ subject?: string;
+ isRead: boolean;
+ isArchived: boolean;
+ readAt?: string | null;
+ archivedAt?: string | null;
+ avatar?: string;
+ tags?: string[];

createdAt: string;
};


type Subscriber = {
  id: string;
  firstName?: string;
  lastName?: string;
  avatar?: string;
  subscriberId: string;
};

type Redirect = {
  url: string;
  target?: '_self' | '_blank' | '_parent' | '_top' | '_unfencedTop';
};

type Action = {
  label: string;
  isCompleted: boolean;
  redirect?: Redirect;
};

Getting Started

To begin migrating to @novu/react, install the package via npm:

npm install @novu/react

Basic Usage

  • Old Implementation
import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
} from "@novu/notification-center";

function Novu() {
  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <PopoverNotificationCenter>
        {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}
export default Novu;
  • New Implementation with @novu/react
import { Inbox } from "@novu/react";

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
      subscriberId="YOUR_SUBSCRIBER_ID"
    />
  );
}

export default Novu;

Notification Center without Bell Icon

The @novu/react package introduces a flexible way to display notifications as a list without the default bell icon. Utilize the Inbox and Notifications components to achieve this functionality.

  • Old Implementation
import { NovuProvider, NotificationCenter } from "@novu/notification-center";

function Novu() {
  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <NotificationCenter colorScheme="dark" />
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
import { Inbox, Notifications } from "@novu/react";

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
    >
      <Notifications />
    </Inbox>
  );
}

export default Novu;

Custom Bell Icon

Customize the bell icon that triggers the notifications popover using the renderBell prop.

  • Old Implementation
import {
  NovuProvider,
  PopoverNotificationCenter,
} from "@novu/notification-center";
import CustomBell from "./CustomBell"; // Your custom bell icon component

function Novu() {
  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <PopoverNotificationCenter colorScheme="dark">
        {({ unseenCount }) => (
          <CustomBell
            unseenCount={unseenCount}
            colorScheme="dark"
            unseenBadgeBackgroundColor="#FFFFFF"
            unseenBadgeColor="#FF0000"
          />
        )}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
import { Inbox } from "@novu/react";
import CustomBell from "./CustomBell"; // Your custom bell icon component

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      renderBell={(unreadCount) => <CustomBell unreadCount={unreadCount} />}
    />
  );
}

export default Novu;

Notification Actions

Handle user interactions with notifications effectively using the action handlers provided by @novu/react.

onNotificationClick

Trigger a callback function when a user clicks on a notification item.

  • Old Implementation
import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
  IMessage,
} from "@novu/notification-center";

function Novu() {
  function handleOnNotificationClick(message: IMessage) {
    // your logic to handle the notification click
    if (message?.cta?.data?.url) {
      window.location.href = message.cta.data.url;
    }
  }

  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <PopoverNotificationCenter
        colorScheme="dark"
        onNotificationClick={handleOnNotificationClick}
      >
        {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
import { Inbox } from "@novu/react";

function Novu() {
  const handleNotificationClick = (notification) => {
    // Your custom logic here
    console.log("Notification clicked:", notification);
  };

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      onNotificationClick={handleNotificationClick}
    />
  );
}

export default Novu;

onPrimaryActionClick and onSecondaryActionClick

Handle primary and secondary actions within a notification explicitly.

  • Old Implementation
import {
  NovuProvider,
  PopoverNotificationCenter,
  IMessage,
  MessageActionStatusEnum,
  useUpdateAction,
  ButtonTypeEnum,
  NotificationBell,
} from "@novu/notification-center";

function Novu() {
  const CustomNotificationCenter = () => {
    const { updateAction } = useUpdateAction();

    const handleOnActionClick = async (
      templateIdentifier: string,
      btnType: ButtonTypeEnum,
      notification: IMessage
    ) => {
      if (templateIdentifier === "friend-request") {
        if (btnType === "primary") {
          /** Call your API to accept the friend request here **/

          /** And then update Novu that this action has been taken, so the user won't see the button again **/
          updateAction({
            messageId: notification._id,
            actionButtonType: btnType,
            status: MessageActionStatusEnum.DONE,
          });
        }
      }
    };

    return (
      <PopoverNotificationCenter
        colorScheme={"dark"}
        onActionClick={handleOnActionClick}
      >
        {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
      </PopoverNotificationCenter>
    );
  };

  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <CustomNotificationCenter />
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
import { Inbox } from "@novu/react";

function Novu() {
  const handlePrimaryActionClick = (notification) => {
    // Handle primary action
    console.log("Primary action clicked:", notification);
  };

  const handleSecondaryActionClick = (notification) => {
    // Handle secondary action
    console.log("Secondary action clicked:", notification);
  };

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      onPrimaryActionClick={handlePrimaryActionClick}
      onSecondaryActionClick={handleSecondaryActionClick}
    />
  );
}

export default Novu;

Avatar Icons

Customize the avatar displayed alongside notifications by specifying an avatar URL at the workflow definition level using @novu/framework.

import { workflow } from "@novu/framework";

workflow("welcome-notification", async (step) => {
  await step.inApp("inbox", async () => ({
    subject: "Welcome to Our Service!",
    body: "We are thrilled to have you onboard.",
    avatar: "https://example.com/path-to-your-avatar-image.png",
  }));
});

Popover Positioning

For advanced positioning and styling of the notifications popover, integrate third-party popover libraries such as Radix UI.

  • Old Implementation
import {
  PopoverNotificationCenter,
  NotificationBell,
  NovuProvider,
} from "@novu/notification-center";

function Novu() {
  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <PopoverNotificationCenter position="left-start" offset={20}>
        {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react and Radix UI as an example
import React from "react";
import * as RadixPopover from "@radix-ui/react-popover";
import { Inbox, Bell, Notifications } from "@novu/react";

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
    >
      <RadixPopover.Root>
        <RadixPopover.Trigger asChild>
          <Bell />
        </RadixPopover.Trigger>
        <RadixPopover.Portal>
          <RadixPopover.Content side="bottom" align="end" sideOffset={10}>
            <Notifications />
            <RadixPopover.Arrow />
          </RadixPopover.Content>
        </RadixPopover.Portal>
      </RadixPopover.Root>
    </Inbox>
  );
}

export default Novu;

Custom Notification Item

Customize the appearance and structure of individual notification items using the renderNotification prop.

  • Old Implementation
import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
} from "@novu/notification-center";

function Novu() {
  return (
    <NovuProvider
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <PopoverNotificationCenter
        colorScheme={colorScheme}
        onNotificationClick={handlerOnNotificationClick}
        onActionClick={handlerOnActionClick}
        listItem={(
          notification,
          handleActionButtonClick,
          handleNotificationClick
        ) => {
          return (
            <a
              href="/"
              onClick={(e) => {
                e.preventDefault();
                handleNotificationClick();
              }}
            >
              {notification.content}
            </a>
          );
        }}
      >
        {({ unseenCount }) => {
          return <NotificationBell unseenCount={unseenCount} />;
        }}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
import { Inbox } from "@novu/react";

function Novu() {
  const renderCustomNotificationItem = (notification) => (
    <div
      className={`notification-item ${notification.isRead ? "read" : "unread"}`}
      onClick={() => notification.read()}
    >
      <img
        src={notification.avatar}
        alt="Avatar"
        className="notification-avatar"
      />
      <div className="notification-content">
        <h4 className="notification-subject">{notification.subject}</h4>
        <p className="notification-body">{notification.body}</p>
      </div>
    </div>
  );

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      renderNotification={renderCustomNotificationItem}
    />
  );
}

export default Novu;

Styling with appearance Prop

Customize the overall look and feel of the notification components using the appearance prop, which supports both CSS objects and class names (including Tailwind CSS classes).

  • Old Implementation
import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
} from "@novu/notification-center";

function Novu() {
  return (
    <NovuProvider
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      styles={{ notifications: { listItem: { layout: { color: "#E3554D" } } } }}
    >
      <PopoverNotificationCenter>
        {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
import { Inbox } from "@novu/react";

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      appearance={{
        baseTheme: {
          variables: {
            colorBackground: "#1F2937",
            colorForeground: "#F9FAFB",
            colorPrimary: "#3B82F6",
            colorSecondaryForeground: "#9CA3AF",
            colorNeutral: "#374151",
          },
        },
        elements: {
          bellIcon: "text-white",
          notificationItem:
            "custom-class bg-gray-800 hover:bg-gray-700 p-4 rounded-md",
          notificationSubject: {
            color: "#E2E8F0",
            fontSize: "18px",
            fontWeight: "bold",
          },
          notificationBody: {
            color: "#A0AEC0",
            fontSize: "14px",
          },
        },
      }}
    />
  );
}

export default Novu;

For more information on appearance customization visit here.

Multiple Tabs Support

Organize notifications into different categories using tabs by leveraging the tags property in workflow definitions and the tabs prop in the Inbox component.

Create Multiple Tabs

  • Old Implementation

After defining the feeds on the Workflow UI, you were able to filter notifications based on the feedIdentifier.

import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
} from "@novu/notification-center";

function Novu() {
  return (
    <NovuProvider
      stores={[
        {
          storeId: "product-updates",
          query: { feedIdentifier: "product-updates" },
        },
        {
          storeId: "user-activity",
          query: { feedIdentifier: "user-activity" },
        },
      ]}
      subscriberId="YOUR_SUBSCRIBER_ID"
      applicationIdentifier="YOUR_APP_ID"
    >
      <PopoverNotificationCenter
        colorScheme="dark"
        tabs={[
          { name: "Product Updates", storeId: "product-updates" },
          { name: "User Activity", storeId: "user-activity" },
        ]}
        colorScheme={colorScheme}
        onNotificationClick={handlerOnNotificationClick}
        onActionClick={handlerOnActionClick}
      >
        {({ unseenCount }) => {
          return (
            <NotificationBell
              colorScheme={colorScheme}
              unseenCount={unseenCount}
            />
          );
        }}
      </PopoverNotificationCenter>
    </NovuProvider>
  );
}

export default Novu;
  • New Implementation with @novu/react
  1. Define multiple workflows with relevant tags.
import { workflow } from "@novu/framework";

workflow(
  "security-alerts",
  async (step) => {
    await step.inApp("inbox", async () => ({
      subject: "Security Alert",
      body: "A new login attempt was detected.",
    }));
  },
  { tags: ["security"] }
);

workflow(
  "promotional-offers",
  async (step) => {
    await step.inApp("inbox", async () => ({
      subject: "Exclusive Offer!",
      body: "Get 50% off on your next purchase.",
    }));
  },
  { tags: ["promotions"] }
);
  1. Assign relevant tags to each workflow to categorize notifications appropriately.
import { Inbox } from "@novu/react";

const tabs = [
  { label: "All Notifications", value: [] },
  { label: "Security", value: ["security"] },
  { label: "Promotions", value: ["promotions"] },
];

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      tabs={tabs}
    />
  );
}

export default Novu;

Localization

Customize the language and text content of the notification components using the localization prop.

import { Inbox } from "@novu/react";

function Novu() {
  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      localization={{
        "inbox.title": "Notificaciones",
        "notifications.emptyNotice": "No tienes nuevas notificaciones",
        "notifications.markAllAsRead": "Marcar todo como leído",
        locale: "es-ES",
      }}
    />
  );
}

export default Novu;

HMAC Encryption

The process remains the same as before. See the detailed guide here.

Handling Notifications

Handle notifications using the methods provided by the notification object.

Marking Notifications as Read

Mark notifications as read using the read method provided by the notification object.

import { Inbox } from "@novu/react";

function Novu() {
  const handleNotificationClick = (notification) => {
    notification.read();
  };

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      onNotificationClick={handleNotificationClick}
    />
  );
}

export default Novu;

Marking Notifications as Unread

Mark notifications as unread using the unread method provided by the notification object.

import { Inbox } from "@novu/react";

function Novu() {
  const handleNotificationClick = (notification) => {
    notification.unread();
  };

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      onNotificationClick={handleNotificationClick}
    />
  );
}

export default Novu;

Marking Notifications as Archive

Mark notifications as archive using the archive method provided by the notification object.

import { Inbox } from "@novu/react";

function Novu() {
  const handleNotificationClick = (notification) => {
    notification.archive();
  };

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      onNotificationClick={handleNotificationClick}
    />
  );
}

export default Novu;

Marking Notifications as Unarchive

Mark notifications as unarchive using the unarchive method provided by the notification object.

import { Inbox } from "@novu/react";

function Novu() {
  const handleNotificationClick = (notification) => {
    notification.unarchive();
  };

  return (
    <Inbox
      applicationIdentifier="YOUR_APP_ID"
      subscriberId="YOUR_SUBSCRIBER_ID"
      onNotificationClick={handleNotificationClick}
    />
  );
}

export default Novu;