category: How toJul 19, 2024

How to Implement React Notifications — Including Examples, Alerts, and Libraries

Learn how to implement effective notifications in React applications. Explore the differences between stateless and stateful notifications, and discover the best libraries and practices for enhancing user engagement and information delivery in your React projects.

Hero Image for React Notification blog

Introduction to React notifications

Notifications in React applications are essential for user engagement and information delivery, and they can vary significantly in complexity and functionality.

What are notifications?

Simple notifications, such as alerts, pop-ups, or toasts, provide immediate, transient messages that require minimal user interaction and are typically used for quick updates or warnings. Modals or dialog boxes, in contrast, are more intrusive and often require user action before proceeding, making them suitable for tasks like form submissions or confirmations. These can also be referred to as STATELESS notifications.

More sophisticated notification systems, like in-app notifications, notification feeds, or inboxes, offer a richer, context-aware experience by maintaining user interaction history and delivering tailored updates. These advanced systems can also be referred to as STATEFUL notifications, dynamic alerts, or personalized messages. Implementing these notifications in React can range from using libraries like react-toastify for simple, ephemeral alerts to using libraries like novu for comprehensive, contextual notification feeds that enhance user engagement and ensure timely delivery of important information.

Important considerations

When adding stateful or stateless notifications, it is crucial to remember that stateless notifications are usually a frontend user experience component, whereas stateful messages and notifications rely on backend and database connectivity. This distinction is vital for determining the architecture and dependencies of your notification system.

Understanding the distinctions between these notification types helps in designing an effective communication strategy within your application.

Types of notifications in React

Notifications can be categorized as stateful or stateless in React software development, particularly in the context of web and mobile applications.

Here’s an explanation of both concepts:

Stateless notifications

Stateless notifications do not keep track of any previous states or sessions. Each notification is independent, and the system sending these notifications does not maintain any information about the user’s interaction with previous notifications.

  • Characteristics
    • Independence: Each notification is sent without regard to any prior notifications.
    • Simplicity: Easier to implement since no state or history needs to be tracked.
    • Use Cases: Suitable for alerts that don’t require context, such as one-time announcements, promotional messages, or simple reminders.

(The GIF is by React-Toastify)

Stateful notifications

Stateful notifications, on the other hand, keep track of the state or history of notifications. The system maintains information about previous interactions, allowing it to send notifications based on past user behavior or states.

  • Characteristics
    • Context-aware: Notifications are tailored based on the user’s interaction history or specific states.
    • Complexity: More complex to implement due to the need to maintain and update user state information.
    • Use Cases: Ideal for notifications that require context, such as progress updates, reminders about incomplete tasks, or notifications about ongoing events.

(Image by Jordan Hughes)

Notification type comparison

 Stateless notificationsStateful notifications
IndependenceEach notification is independentNotifications are context-aware and related
ComplexitySimpler to implementMore complex due to state tracking
ContextNo user-specific contextContext-aware, tailored based on user history
Use casesOne-time alerts, general announcementsProgress updates, task reminders
ImplementationNo need to maintain a user interaction historyRequires maintaining and updating user states

Setting up React stateless notifications

There are two common types of stateless notifications:

Pop-up notifications

Pop-up notifications (or toast notifications, snackbars, alerts) are non-intrusive graphical elements that communicate events to the user without requiring immediate action. They usually disappear automatically after a short time but can be stored in a widget for later access.

Here are some visual examples:

Modal notifications

Modal notifications, often referred to as dialog boxes, dialogs, popup windows, popups, lightboxes, alert dialogs, confirmation dialogs, overlays, message boxes, prompts, modal windows, or inline dialogs, are interfaces that temporarily interrupt the user’s workflow to focus on specific information or actions.

These terms highlight different aspects of modals, such as requiring user input, confirming actions, or displaying important messages, and are used interchangeably depending on the design language or framework.

Visual modal notification examples

Image example of visual modal notification from Bootstrap's documentation

(Image from Bootstrap’s Documentation)

(Image Source)

Open source stateless notifications solutions

These libraries enable the display of temporary messages, alerts, and other notifications in a way that is efficient, customizable, and easy to integrate into existing projects. Stateless notifications are particularly advantageous because they simplify the development process, reduce server load, and enhance the user experience by offering real-time feedback directly in the browser. Here are some of the most popular and feature-rich React libraries available for implementing such notifications, ensuring developers have access to a range of options suited to various needs and preferences.

react-toastify (GitHub) (Demo)

react-toastify enables you to add notifications to your app with ease. It’s highly customizable and offers a variety of features including toast notification styles, animation options, and automatic dismissal.

  • GitHub stars: 12.4k
  • Features
    • Easy to set up
    • Easy to customize
    • RTL support
    • Swipe to close
    • Customize the swipe direction
    • Use an animation of your choice. (Works well with animate.css)
    • Can display a React component inside the toast
    • Has onOpen and onClose hooks (Both can access the props passed to the React component rendered inside the toast)
    • Can remove a toast programmatically
    • Define behavior per toast
    • Pause toast when the window loses focus
    • Progress bar to display the remaining time
    • Possibility to update a toast
    • Control over the progress bar
    • Limit the number of toasts displayed at the same time
    • Dark mode
    • Pause timer programmatically
    • Stacked notifications
    • And more

react-hot-toast (GitHub) (Demo)

react-hot-toast is an attractive library that lets you create notifications with beautiful designs and animations.

  • GitHub stars: 9.5k
  • Features
    • Customizable
    • Promise API – Automatic loader from a promise
    • Lightweight – less than 5kb including styles
    • Accessible

notistack (GitHub) (Demo)

notistack is a library that makes it easy to display snackbar notifications. It’s built on top of Material-UI and offers various customization and configuration options.

  • GitHub stars: 3.8k
  • Features
    • Supports snackbar notifications
    • Can render toasts outside of the React tree
    • Supports custom toast renderers
    • Can enqueue and dismiss notifications programmatically
    • Offers rich customization options for positioning, styling, and behavior

    react-notification-system (GitHub) (Demo)

    react-notification-system is a complete and totally customizable component for notifications in React.

    • GitHub stars: 2.4k
    • Features
      • Supports custom toast animations
      • Can render toasts inside modals
      • Supports custom toast renderers
      • Easily integrates with existing React components
      • Provides a robust API for controlling notifications programmatically

      react-toast-notifications (GitHub) (Demo)

      A configurable, composable, toast notification system for React.

      • GitHub stars: 2.2k
      • Features
        • Supports custom toast themes
        • Can render toasts inside custom elements
        • Supports custom toast renderers
        • Provides a flexible and composable API for building notification systems
        • Offers fine-grained control over toast placement, appearance, and behavior

        reapop (GitHub) (Demo)

        A simple and customizable React notification system

        • GitHub stars: 1.5k
        • Features
          • Supports multiple notification types (e.g., toasts, snackbars, alerts)
          • Can render notifications inside custom elements
          • Supports custom notification renderers
          • Provides a simple API for creating, updating, and removing notifications
          • Offers flexible configuration options for styling and behavior

          react-notifications-component (GitHub) (Demo)

          This library boasts a highly configurable notification component that supports various notification types, animations, and positions.

          • GitHub stars: 1.3k
          • Features
            • Touch support
            • Responsive notifications
            • Standard notification types
            • Custom notification types
            • Custom notification content
            • Dismissible (touch, click, timeout)
            • Customizable transitions
            • Small library footprint
            • Provides a wide range of options for notification placement and styling

            react-redux-toastr (GitHub) (Demo)

            react-redux-toastr is a toastr message generator for React and Redux, allowing for smooth integration with Redux applications.

            • GitHub stars: 750
            • Features
              • Provides integration with Redux
              • Can display toasts based on Redux actions
              • Can display toasts with custom Redux state
              • Supports complex workflows and state management through Redux
              • Offers a powerful API for controlling toasts within a Redux ecosystem

              notiflix (GitHub) (Demo)

              notiflix is a pure JavaScript library for client-side non-blocking notifications, popup boxes, loading indicators, and more that makes your web projects much better.

              • GitHub stars: 620
              • Features
                • Provides a comprehensive notification library with support for toasts, snackbars, alerts, modals, and more
                • Supports multiple themes and customization options
                • Provides a variety of pre-built notification animations
                • Lightweight and easy to integrate into existing projects
                • Offers a robust API for controlling notifications programmatically

                react-modal (GitHub) (Demo)

                react-modal is an official accessible modal dialog component for React.

                • GitHub stars: 7.3k
                • Features
                  • Provides a comprehensive notification library with support for toasts, snackbars, alerts, modals, and more
                  • Supports multiple themes and customization options
                  • Provides a variety of pre-built notification animations
                  • Fully accessible and compliant with WAI-ARIA guidelines
                  • Highly customizable and easy to use within React applications

                  react-aria (GitHub

                  react-aria is part of react-spectrum, a collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.

                  • GitHub stars: 12.3k
                  • Features
                      • Accessibility and behavior is implemented according to WAI-ARIA authoring practices
                      • All components are designed to work with mouse, touch, and keyboard interactions. Built with responsive design principles
                      • Support for over 30 languages included out of the box
                      • Supports custom themes and automatically adapt for dark mode

                       

                      Recommendations for stateless notifications

                      We could recommend the react-toastify library. It is the most popular library for displaying toast messages in React applications. It provides a simple and flexible API for creating customizable toast messages, including success, error, warning, and info types.

                      The library allows you to set the duration, position, and styling of the toasts.

                      It’s highly recommended for its ease of use, extensive customization options, and advanced features. Its intuitive API and comprehensive documentation make integration straightforward.

                      You can tailor the appearance and behavior of toasts to match your application’s design, with options for custom animations, RTL support, swipe-to-close functionality, and more.

                      The library supports programmatic control over toasts, including pausing, updating, and removing them. It is actively maintained by the developer community and compatible with the latest React versions. React Toastify’s popularity is reflected in its large number of GitHub stars, indicating widespread use and community support.

                      Stateless notifications implementation

                      Requirements

                      React version >= 16.8 or above

                      Installation

                      With npm:

                      1npm install --save react-toastify

                      With yarn:

                      1yarn add react-toastify

                      The gist

                      1  import React from 'react';
                      2  import { ToastContainer, toast } from 'react-toastify'; 
                      3
                      4  import 'react-toastify/dist/ReactToastify.css';
                      5  // minified version is also included
                      6  // import 'react-toastify/dist/ReactToastify.min.css';
                      7
                      8  function App(){
                      9    const notify = () => toast("Wow so easy !");
                      10
                      11    return (
                      12      <div>
                      13        <button onClick={notify}>Notify !</button>
                      14        <ToastContainer />
                      15      </div>
                      16    );
                      17  }

                      Note: Remember to render the ToastContainer once in your application tree.
                      If you can’t figure out where to put it, rendering it in the application root would be the best bet.

                      Best practices

                      Consistent usage

                      • Uniform toast placement: Ensure all toast notifications appear in a consistent position across your application (e.g., top-right, bottom-center). This helps users know where to look for important messages.
                      • Duration management: Set appropriate durations for different types of notifications. For instance, error messages might need more time to be read and understood compared to success messages.
                      • Styling Consistency: Maintain consistent styling for all toast notifications to align with your application’s design language. This includes colors, fonts, and animations.

                      User experience

                      • Non-intrusive notifications: Keep notifications non-intrusive. Avoid blocking the main content or important user interactions. The toast messages should be informative but not disruptive.
                      • Clear messaging: Ensure the content of the notifications is clear and concise. Users should understand the message without confusion.
                      • Accessibility: Make sure that toast notifications are accessible. Use ARIA roles and properties to ensure screen readers can announce the toast messages.

                      Customization

                      Override CSS variables

                      Below the list of the CSS variables that are exposed by the library. You can accomplish a lot by overriding some of them.

                      1:root {
                      2  --toastify-color-light: #fff;
                      3  --toastify-color-dark: #121212;
                      4  --toastify-color-info: #3498db;
                      5  --toastify-color-success: #07bc0c;
                      6  --toastify-color-warning: #f1c40f;
                      7  --toastify-color-error: #e74c3c;
                      8  --toastify-color-transparent: rgba(255, 255, 255, 0.7);
                      9
                      10  --toastify-icon-color-info: var(--toastify-color-info);
                      11  --toastify-icon-color-success: var(--toastify-color-success);
                      12  --toastify-icon-color-warning: var(--toastify-color-warning);
                      13  --toastify-icon-color-error: var(--toastify-color-error);
                      14
                      15  --toastify-toast-width: 320px;
                      16  --toastify-toast-background: #fff;
                      17  --toastify-toast-min-height: 64px;
                      18  --toastify-toast-max-height: 800px;
                      19  --toastify-font-family: sans-serif;
                      20  --toastify-z-index: 9999;
                      21
                      22  --toastify-text-color-light: #757575;
                      23  --toastify-text-color-dark: #fff;
                      24
                      25  //Used only for colored theme
                      26  --toastify-text-color-info: #fff;
                      27  --toastify-text-color-success: #fff;
                      28  --toastify-text-color-warning: #fff;
                      29  --toastify-text-color-error: #fff;
                      30
                      31  --toastify-spinner-color: #616161;
                      32  --toastify-spinner-color-empty-area: #e0e0e0;
                      33
                      34  // Used when no type is provided
                      35  // toast("**hello**")
                      36  --toastify-color-progress-light: linear-gradient(
                      37    to right,
                      38    #4cd964,
                      39    #5ac8fa,
                      40    #007aff,
                      41    #34aadc,
                      42    #5856d6,
                      43    #ff2d55
                      44  );
                      45  // Used when no type is provided
                      46  --toastify-color-progress-dark: #bb86fc;
                      47  --toastify-color-progress-info: var(--toastify-color-info);
                      48  --toastify-color-progress-success: var(--toastify-color-success);
                      49  --toastify-color-progress-warning: var(--toastify-color-warning);
                      50  --toastify-color-progress-error: var(--toastify-color-error);
                      51
                      52  // used to control the opacity of the progress trail
                      53  --toastify-color-progress-bgo: .2;
                      54}

                      Override existing CSS classes

                      If overriding the CSS variables is not enough for you, you can override the existing CSS classes. Below, a list of the CSS classes used (classes used for animation and media query are omitted)

                      1/** Used to define container behavior: width, position: fixed etc... **/
                      2.Toastify__toast-container {
                      3}
                      4
                      5/** Used to define the position of the ToastContainer **/
                      6.Toastify__toast-container--top-left {
                      7}
                      8.Toastify__toast-container--top-center {
                      9}
                      10.Toastify__toast-container--top-right {
                      11}
                      12.Toastify__toast-container--bottom-left {
                      13}
                      14.Toastify__toast-container--bottom-center {
                      15}
                      16.Toastify__toast-container--bottom-right {
                      17}
                      18
                      19/** Classes for the displayed toast **/
                      20.Toastify__toast {
                      21}
                      22.Toastify__toast--rtl {
                      23}
                      24.Toastify__toast-body {
                      25}
                      26
                      27/** Used to position the icon **/
                      28.Toastify__toast-icon {
                      29}
                      30
                      31/** handle the notification color and the text color based on the theme **/
                      32.Toastify__toast-theme--dark {
                      33}
                      34.Toastify__toast-theme--light {
                      35}
                      36.Toastify__toast-theme--colored.Toastify__toast--default {
                      37}
                      38.Toastify__toast-theme--colored.Toastify__toast--info {
                      39}
                      40.Toastify__toast-theme--colored.Toastify__toast--success {
                      41}
                      42.Toastify__toast-theme--colored.Toastify__toast--warning {
                      43}
                      44.Toastify__toast-theme--colored.Toastify__toast--error {
                      45}
                      46
                      47.Toastify__progress-bar {
                      48}
                      49.Toastify__progress-bar--rtl {
                      50}
                      51.Toastify__progress-bar-theme--light {
                      52}
                      53.Toastify__progress-bar-theme--dark {
                      54}
                      55.Toastify__progress-bar--info {
                      56}
                      57.Toastify__progress-bar--success {
                      58}
                      59.Toastify__progress-bar--warning {
                      60}
                      61.Toastify__progress-bar--error {
                      62}
                      63/** colored notifications share the same progress bar color **/
                      64.Toastify__progress-bar-theme--colored.Toastify__progress-bar--info,
                      65.Toastify__progress-bar-theme--colored.Toastify__progress-bar--success,
                      66.Toastify__progress-bar-theme--colored.Toastify__progress-bar--warning,
                      67.Toastify__progress-bar-theme--colored.Toastify__progress-bar--error {
                      68}
                      69
                      70/** Classes for the close button. Better use your own closeButton **/
                      71.Toastify__close-button {
                      72}
                      73.Toastify__close-button--default {
                      74}
                      75.Toastify__close-button > svg {
                      76}
                      77.Toastify__close-button:hover,
                      78.Toastify__close-button:focus {
                      79}

                      Here you can learn more about how to style your stateless notifications.

                      Testing

                      Testing toast notifications in a React application using react-toastify involves checking that the toasts appear correctly and contain the expected content. You can achieve this using testing libraries such as Jest and React Testing Library.

                      Keep reading for a step-by-step guide on how to include testing for react-toastify.

                      • Setting up testing environment
                        • Ensure you have the necessary testing libraries installed:
                      1npm install --save-dev @testing-library/react @testing-library/jest-dom jest
                      • Writing tests for toast notifications
                        • Render the component with toasts: Make sure the component that triggers the toast notifications is rendered in the test.
                        • Trigger toasts: Simulate the actions that would cause the toast notifications to appear.
                        • Assert toasts: Verify that the toasts appear with the correct content and attributes.

                          Here’s an example test file:

                      1// App.test.jsx
                      2import React from 'react';
                      3import { render, screen, fireEvent } from '@testing-library/react';
                      4import '@testing-library/jest-dom/extend-expect';
                      5import App from './App';  // Import your component
                      6import { ToastContainer } from 'react-toastify';
                      7import 'react-toastify/dist/ReactToastify.css';
                      8
                      9test('displays toast notification on button click', () => {
                      10  render(
                      11    <>
                      12      <ToastContainer />
                      13      <App />
                      14    </>
                      15  );
                      16
                      17  // Simulate button click to trigger the toast
                      18  fireEvent.click(screen.getByText('Notify !'));
                      19
                      20  // Assert the toast notification appears
                      21  expect(screen.getByText('Wow so easy !')).toBeInTheDocument();
                      22});
                      23
                      24test('toast notification disappears after duration', async () => {
                      25  render(
                      26    <>
                      27      <ToastContainer />
                      28      <App />
                      29    </>
                      30  );
                      31
                      32  // Simulate button click to trigger the toast
                      33  fireEvent.click(screen.getByText('Notify !'));
                      34
                      35  // Assert the toast notification appears
                      36  expect(screen.getByText('Wow so easy !')).toBeInTheDocument();
                      37
                      38  // Wait for the toast to disappear (assuming the default duration is 5000ms)
                      39  await new Promise((r) => setTimeout(r, 5000));
                      40
                      41  // Assert the toast notification is removed
                      42  expect(screen.queryByText('Wow so easy !')).not.toBeInTheDocument();
                      43});
                      44

                      Explanation

                      1. Setup the test environment: The render function from @testing-library/react is used to render the App component along with the ToastContainer.
                      2. Trigger the toast: The fireEvent.click function simulates a button click to trigger the toast notification.
                      3. Assertions
                        • expect(screen.getByText('Wow so easy !')).toBeInTheDocument(); checks that the toast notification with the specified text appears in the document.
                        • await new Promise((r) => setTimeout(r, 5000)); introduces a delay to wait for the toast to disappear.
                        • expect(screen.queryByText('Wow so easy !')).not.toBeInTheDocument(); verifies that the toast notification is no longer in the document after the duration.

                      Additional tests

                      You can add more tests to cover different scenarios, such as:

                      • Different Toast Types: Test success, error, warning, and info toasts.
                      • Custom Duration: Test toasts with custom durations.
                      • Positioning: Verify that toasts appear in the correct position.

                      Integration with CI/CD

                      Ensure that your tests are integrated with your CI/CD pipeline to automatically run these tests on every push or pull request. This helps catch any issues early and maintains the reliability of your notifications

                      In case you would like to get even more into how to customize and have a better understanding overall, consider visiting the official documentation.

                      Setting Up React stateful notifications

                      Stateful notifications track the status of notifications, distinguishing between those that have been read and those that are new. This status is managed on both the client side (front-end) and the server side (back-end).

                      Stateful notifications are delivered through various channels such as Email, SMS, Chat, Push, and In-App notifications. The choice of channels depends on your product’s use case and available resources.

                      In-App notifications

                      In-App notifications, also known as embedded alerts, notification feeds, inboxes, or integrated messages, are stateful notification channels used in React applications to provide real-time updates directly within the application’s interface.

                      These notifications enhance user engagement by delivering timely information in a non-disruptive manner. While they are visible during active use of the application, they can also be stored in a dedicated section for later access.

                      Open source stateful notifications solutions

                      novu (GitHub) (Demo)

                      novu provides a unified API that makes it simple to send notifications through multiple channels, including In-App, Push, Email, SMS, and Chat. With Novu, you can create custom workflows and define conditions for each channel, ensuring that your notifications are delivered in the most effective way possible.

                      • GitHub stars: 33.8k
                      • Features:
                        • Pre-built UI components
                          Novu provides pre-built, ready-to-use, and customizable UI components
                          for in-app notifications. This can significantly reduce development time and effort.
                        • Headless option
                          For developers who prefer more control, Novu offers a headless component that can be used to add in-app notifications to any platform. This flexibility allows for greater customization.
                        • Multi-tab support
                          Novu’s in-app notifications provide a multi-tab notification experience, ensuringconsistency across different tabs of your application.
                        • Security
                          You can secure your connection using HMAC encryption. This adds an extra layer of security to your in-app notifications.
                        • Backend SDK methods
                          Novu provides backend SDK methods to support in-app notifications. This allows for seamless integration between your backend and the in-app notification system.
                        • Integration with multiple channels
                          Novu supports multiple notification channels including email, SMS, push, and chat, in addition to in-app notifications. This allows for a comprehensive notification strategy across various channels.
                        • Workflow management
                          Novu allows you to create notification workflows, which can include
                          in-app notifications as part of a larger notification strategy.
                        • Analytics and logs
                          Novu provides insights into logs and analytics, which can help you optimize your notification experience.
                        • Scalability
                          Novu’s infrastructure is designed to handle high-volume notification delivery and storage.
                        • Compliance and security
                          Novu prioritizes compliance with industry standards and maintains robust security measures to protect your data.
                        • Inbox component
                          The Inbox component, also referred to as a widget, provides a smooth and
                          attractive in-app notification experience for users in web
                          applications.
                        • Multi-device real-time notifications
                          Novu’s in-app component offers a multi-device real-time notification experience powered by web sockets.
                        • Customizable UI
                          The in-app component is highly customizable to match your product’s design system.
                        • Subscriber preferences management
                          Users can manage their notification preferences directly from the in-app component.
                        • Multiple programming language support
                          Novu supports integration with multiple programming languages, including
                          Python, Go, Ruby, PHP, and Kotlin, allowing developers to create
                          applications with complex notification systems.
                        • UI libraries support
                          Novu provides support for various UI libraries, including:
                        • Feeds
                          In-app notifications can be grouped into different feeds or tabs. Each message can be assigned to one specific feed.
                        • Notification actions
                          The in-app component supports notification actions, which are CTA buttons below the notification content that can be clicked to perform actions or external API calls.
                        • Redirect URL
                          Notifications can be configured to redirect users to a specific URL when clicked.

                      Recommendations for stateful notifications

                      Before building a custom React notification system, it’s crucial to understand the potential challenges and hidden costs involved.

                      A comprehensive React notification system often needs to include web push, mobile push, SMS, and email notifications. Each type requires specific implementations and permission-based systems.

                      Moreover, handling user data for SMS and email must comply with privacy laws like GDPR and CCPA.

                      Developers typically spend only about 31% of their time writing code when building a custom notification system.

                      The remaining time is consumed by managing multiple custom implementations for different browsers and devices, which adds to the workload.

                      After implementation, A/B testing is necessary to optimize content and timing, requiring proper data storage and analysis.

                      While custom solutions offer control over design and implementation, they can be more trouble than they are worth. Unless notifications are central to your business, building and maintaining an in-house system is a significant commitment for any development team.

                      Novu is the only open-source solution that provides an end-to-end solution. It offers options to self-host the entire infrastructure, use only the In-App ready-to-use or headless component functionality, or handle all your current and future notification needs.

                      Stateful notifications implementation

                      Requirements

                      React version >= 16.8 or above

                      Installation

                      With npm:

                      1npm install @novu/notification-center

                      With yarn:

                      1yarn add novu/notification-center

                      The gist

                      1import {
                      2  NovuProvider,
                      3  PopoverNotificationCenter,
                      4  NotificationBell,
                      5  IMessage,
                      6} from "@novu/notification-center";
                      7
                      8function Novu() {
                      9  return (
                      10    <NovuProvider
                      11      subscriberId={"SUBSCRIBER_ID"}
                      12      applicationIdentifier={"APPLICATION_IDENTIFIER"}
                      13    >
                      14      <PopoverNotificationCenter colorScheme="dark">
                      15        {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
                      16      </PopoverNotificationCenter>
                      17    </NovuProvider>
                      18  );
                      19}
                      • NovuProvider: This component wraps your application and provides the necessary context for Novu to work.
                      • PopoverNotificationCenter: This component creates a popover that contains the notification center.
                      • NotificationBell: This component renders the notification bell icon, which displays the number of unseen notifications.

                        Important notes

                        • Replace "SUBSCRIBER_ID" with the actual subscriber ID for the user.
                        • Replace "APPLICATION_IDENTIFIER" with your Novu application identifier.

                      Best practices

                      Consistent usage

                      • Ensure that the NovuProvider wraps your entire application to provide the necessary context for the notification system.
                      • Use the PopoverNotificationCenter to create a popover containing the notification center, and the NotificationBell to render the notification bell icon.
                      • Replace "SUBSCRIBER_ID" and "APPLICATION_IDENTIFIER" with the actual values for your users and application.
                      • Customize the colorScheme prop on PopoverNotificationCenter to fit your application’s design.

                      User experience

                      • Keep the notification bell icon visible and easily accessible to users.
                      • Use onNotificationClick to handle what happens when a user clicks on a notification, ensuring a seamless navigation experience.
                      • Utilize onActionClick to manage actions within notifications, such as accepting friend requests or other interactive elements.
                      • Implement HMAC encryption to secure the subscriberId and prevent malicious access to user feeds.
                      • Provide real-time updates using the useSocket hook to keep users informed about new notifications immediately.

                      Customization

                      PopoverNotificationCenter customization

                      <PopoverNotificationCenter> is a pre built notification center component that can be used to show notifications in your application. It is a popover component that can be placed anywhere in your application. A bell will appear on the page. On click of that bell, a popover will appear. Checkout all customizations and available props. If you are looking for a component without popover use regular <NotificationCenter> component. Checkout props of this component. Few selected customization options are explained below:-

                      Implementing custom bell icon

                      It is common that you might have a special set of icons you use within your application and you will want to replace the default: NotificationBell coming from our library.

                      For this you can easily switch the NotificationBell with your own bell. Just make sure you pass the unseenCount param inside and use it accordingly.

                      1<PopoverNotificationCenter colorScheme="dark">
                      2  {({ unseenCount }) => (
                      3    <CustomBell
                      4      unseenCount={unseenCount}
                      5      colorScheme="dark"
                      6      unseenBadgeBackgroundColor="#FFFFFF"
                      7      unseenBadgeColor="#FF0000"
                      8    />
                      9  )}
                      10</PopoverNotificationCenter>

                      Dark mode support

                      To support dark mode in your application the notification center component can receive a colorScheme prop that can receive either dark or light mode.

                      1<PopoverNotificationCenter colorScheme={"dark" || "light"}>
                      2  {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
                      3</PopoverNotificationCenter>
                      4

                      Popover positioning

                      Use position  and offset prop to position the popover relative to the Bell icon

                      1<PopoverNotificationCenter position="left-start" offset={20}>
                      2  {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
                      3</PopoverNotificationCenter>
                      4

                      Header, footer and empty state

                      Use header, footer and emptyState prop to customize the header, footer and empty state of notification center.

                      1const Header = () => {
                      2  return <span> My custom header </span>;
                      3};
                      4
                      5const Footer = () => {
                      6  return <span> My custom footer </span>;
                      7};
                      8
                      9const EmptyState = () => {
                      10  return <span> My custom empty state </span>;
                      11};
                      12
                      13<PopoverNotificationCenter
                      14  colorScheme="dark"
                      15  header={() => <Header />}
                      16  footer={() => <Footer />}
                      17  emptyState={<EmptyState />}
                      18>
                      19  {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
                      20</PopoverNotificationCenter>;
                      21

                      Notification item

                      Use the listItem prop to show custom notification items.

                      1<PopoverNotificationCenter
                      2  colorScheme={colorScheme}
                      3  onNotificationClick={handlerOnNotificationClick}
                      4  onActionClick={handlerOnActionClick}
                      5  listItem={(
                      6    notification,
                      7    handleActionButtonClick,
                      8    handleNotificationClick
                      9  ) => {
                      10    return (
                      11      <a
                      12        href="/"
                      13        onClick={(e) => {
                      14          e.preventDefault();
                      15          handleNotificationClick();
                      16        }}
                      17      >
                      18        {notification.content}
                      19      </a>
                      20    );
                      21  }}
                      22>
                      23  {({ unseenCount }) => {
                      24    return <NotificationBell unseenCount={unseenCount} />;
                      25  }}
                      26</PopoverNotificationCenter>

                      Customize the UI language

                      If you want to use a language other than english for the UI, the NovuProvider component can accept an optional i18n prop.

                      1<NovuProvider
                      2  subscriberId={"USER_ID"}
                      3  applicationIdentifier={"APPLICATION_IDENTIFIER"}
                      4  i18n="en"
                      5>
                      6  <PopoverNotificationCenter colorScheme="dark">
                      7    {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
                      8  </PopoverNotificationCenter>
                      9</NovuProvider>

                      The i18n prop can accept 2 different types of values 2 letter language string.

                      1i18n = "en";

                      Translation object

                      1i18n={{
                      2  // Make sure that the following is a proper language code,
                      3  // since this is used by Intl.RelativeTimeFormat in order to calculate the relative time for each notification
                      4  // <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat>
                      5  lang: "de",
                      6
                      7  translations: {
                      8    poweredBy: "von",
                      9    markAllAsRead: "Alles als gelesen markieren",
                      10    notifications: "Benachrichtigungen",
                      11    settings: "Einstellungen",
                      12  },
                      13}}
                      14
                      15

                      Customization using styles prop

                      styles prop can be used to customize styling of each component of notification center.

                      Read more about styles prop

                      1export const styles = {
                      2  bellButton: {
                      3    root: {
                      4      marginTop: "20px",
                      5      svg: {
                      6        color: "#AFE1AF",
                      7        fill: "white",
                      8        minWidth: "20px",
                      9        minHeight: "20px",
                      10      },
                      11    },
                      12    dot: {
                      13      rect: {
                      14        fill: "white",
                      15        strokeWidth: "0",
                      16        width: "3px",
                      17        height: "3px",
                      18        x: 10,
                      19        y: 2,
                      20      },
                      21    },
                      22  },
                      23};

                      Customizing styles

                      1const primaryColor = "white";
                      2const secondaryColor = "#AFE1AF";
                      3const primaryTextColor = "#0C0404";
                      4const secondaryTextColor = "#494F55";
                      5const unreadBackGroundColor = "#869F9F";
                      6const primaryButtonBackGroundColor = unreadBackGroundColor;
                      7const secondaryButtonBackGroundColor = "#C6DFCD";
                      8const dropdownBorderStyle = "2px solid #AFE1AF";
                      9const tabLabelAfterStyle = "#AFE1AF !important";
                      10const ncWidth = "350px !important";
                      11
                      12export const styles = {
                      13  bellButton: {
                      14    root: {
                      15      marginTop: "20px",
                      16      svg: {
                      17        color: secondaryColor,
                      18        fill: primaryColor,
                      19        minWidth: "20px",
                      20        minHeight: "20px"
                      21      }
                      22    },
                      23    dot: {
                      24      rect: {
                      25        fill: primaryColor,
                      26        strokeWidth: "0",
                      27        width: "3px",
                      28        height: "3px",
                      29        x: 10,
                      30        y: 2
                      31      }
                      32    }
                      33  },
                      34  unseenBadge: {
                      35    root: { color: primaryTextColor, background: secondaryColor }
                      36  },
                      37  popover: {
                      38    root: { zIndex: -99 },
                      39    arrow: {
                      40      backgroundColor: primaryColor,
                      41      borderLeftColor: secondaryColor,
                      42      borderTopColor: secondaryColor
                      43    },
                      44    dropdown: {
                      45      border: dropdownBorderStyle,
                      46      borderRadius: "10px",
                      47      marginTop: "25px",
                      48      maxWidth: ncWidth
                      49    }
                      50  },
                      51  header: {
                      52    root: {
                      53      backgroundColor: primaryColor,
                      54      "&:hover": { backgroundColor: primaryColor },
                      55      cursor: "pointer",
                      56      color: primaryTextColor
                      57    },
                      58    cog: { opacity: 1 },
                      59    markAsRead: {
                      60      color: primaryTextColor,
                      61      fontSize: "14px"
                      62    },
                      63    title: { color: primaryTextColor },
                      64    backButton: {
                      65      color: primaryTextColor
                      66    }
                      67  },
                      68  layout: {
                      69    root: {
                      70      background: primaryColor,
                      71      maxWidth: ncWidth
                      72    }
                      73  },
                      74  loader: {
                      75    root: {
                      76      stroke: primaryTextColor
                      77    }
                      78  },
                      79  accordion: {
                      80    item: {
                      81      backgroundColor: secondaryColor,
                      82      ":hover": {
                      83        backgroundColor: secondaryColor
                      84      }
                      85    },
                      86    content: {
                      87      backgroundColor: secondaryColor,
                      88      borderBottomLeftRadius: "7px",
                      89      borderBottomRightRadius: "7px"
                      90    },
                      91    control: {
                      92      ":hover": {
                      93        backgroundColor: secondaryColor
                      94      },
                      95      color: primaryTextColor,
                      96      title: {
                      97        color: primaryTextColor
                      98      }
                      99    },
                      100    chevron: {
                      101      color: primaryTextColor
                      102    }
                      103  },
                      104  notifications: {
                      105    root: {
                      106      ".nc-notifications-list-item": {
                      107        backgroundColor: secondaryColor
                      108      }
                      109    },
                      110    listItem: {
                      111      layout: {
                      112        borderRadius: "7px",
                      113        color: primaryTextColor,
                      114        "div:has(> .mantine-Avatar-root)": {
                      115          border: "none",
                      116          width: "20px",
                      117          height: "20px",
                      118          minWidth: "20px"
                      119        },
                      120        ".mantine-Avatar-root": {
                      121          width: "20px",
                      122          height: "20px",
                      123          minWidth: "20px"
                      124        },
                      125        ".mantine-Avatar-image": {
                      126          width: "20px",
                      127          height: "20px",
                      128          minWidth: "20px"
                      129        }
                      130      },
                      131      timestamp: { color: secondaryTextColor, fontWeight: "bold" },
                      132      dotsButton: {
                      133        path: {
                      134          fill: primaryTextColor
                      135        }
                      136      },
                      137      unread: {
                      138        "::before": { background: unreadBackGroundColor }
                      139      },
                      140      buttons: {
                      141        primary: {
                      142          background: primaryButtonBackGroundColor,
                      143          color: primaryTextColor,
                      144          "&:hover": {
                      145            background: primaryButtonBackGroundColor,
                      146            color: secondaryTextColor
                      147          }
                      148        },
                      149        secondary: {
                      150          background: secondaryButtonBackGroundColor,
                      151          color: secondaryTextColor,
                      152          "&:hover": {
                      153            background: secondaryButtonBackGroundColor,
                      154            color: secondaryTextColor
                      155          }
                      156        }
                      157      }
                      158    }
                      159  },
                      160  actionsMenu: {
                      161    item: { "&:hover": { backgroundColor: secondaryColor } },
                      162    dropdown: {
                      163      backgroundColor: primaryColor
                      164    },
                      165    arrow: {
                      166      backgroundColor: primaryColor,
                      167      borderTop: "0",
                      168      borderLeft: "0"
                      169    }
                      170  },
                      171  preferences: {
                      172    item: {
                      173      title: { color: primaryTextColor },
                      174      divider: { borderTopColor: primaryColor },
                      175      channels: { color: secondaryTextColor },
                      176      content: {
                      177        icon: { color: primaryTextColor },
                      178        channelLabel: { color: primaryTextColor },
                      179        success: { color: primaryTextColor }
                      180      }
                      181    }
                      182  },
                      183  tabs: {
                      184    tabLabel: {
                      185      "::after": { background: tabLabelAfterStyle },
                      186      ":hover": { color: tabLabelAfterStyle },
                      187      "[data-active]": { color: tabLabelAfterStyle }
                      188    },
                      189    tabsList: { borderBottomColor: primaryColor }
                      190  }
                      191};

                      Customization using hooks

                      If you don’t want to use our pre built component then, you can build your own notification center using hooks. @novu/notification-center react package provides many hooks like useNotifications, useFetchNotifications, useUpdateUserPreferences. Checkout all available hooks. All the hooks should be defined inside the NovuProvider component. Most of the hooks are based on the @tanstack/react-query library.

                      To learn more about how to customize and style your In-App Inbox component, visit this page.

                      Testing

                      You can use testing libraries like React Testing Library and Jest.
                      Here is an example of how you can write tests for this component:

                      1. Install the necessary dependencies

                      1npm install @testing-library/react @testing-library/jest-dom jest

                      2. Create a test file for the Novu component, for example Novu.test.js

                      1import React from 'react';
                      2import { render, screen } from '@testing-library/react';
                      3import '@testing-library/jest-dom/extend-expect';
                      4import { NovuProvider, PopoverNotificationCenter, NotificationBell } from '@novu/notification-center';
                      5import Novu from './Novu';
                      6
                      7jest.mock('@novu/notification-center', () => ({
                      8  NovuProvider: jest.fn(({ children }) => <div>{children}</div>),
                      9  PopoverNotificationCenter: jest.fn(({ children }) => children({ unseenCount: 5 })),
                      10  NotificationBell: jest.fn(({ unseenCount }) => <div>{`Unseen Count: ${unseenCount}`}</div>),
                      11}));
                      12
                      13describe('Novu component', () => {
                      14  test('renders NovuProvider with correct props', () => {
                      15    render(<Novu />);
                      16    expect(NovuProvider).toHaveBeenCalledWith(
                      17      expect.objectContaining({
                      18        subscriberId: 'SUBSCRIBER_ID',
                      19        applicationIdentifier: 'APPLICATION_IDENTIFIER',
                      20      }),
                      21      expect.anything()
                      22    );
                      23  });
                      24
                      25  test('renders PopoverNotificationCenter with dark colorScheme', () => {
                      26    render(<Novu />);
                      27    expect(PopoverNotificationCenter).toHaveBeenCalledWith(
                      28      expect.objectContaining({
                      29        colorScheme: 'dark',
                      30      }),
                      31      expect.anything()
                      32    );
                      33  });
                      34
                      35  test('renders NotificationBell with correct unseenCount', () => {
                      36    render(<Novu />);
                      37    expect(screen.getByText('Unseen Count: 5')).toBeInTheDocument();
                      38  });
                      39});
                      40

                      3. Run your tests

                      1npm test

                      Explanation

                      This test setup does the following:

                      • Mocks the NovuProvider, PopoverNotificationCenter, and NotificationBell components from @novu/notification-center.
                      • Checks if the NovuProvider component is rendered with the correct props.
                      • Checks if the PopoverNotificationCenter component is rendered with the dark color scheme.
                      • Checks if the NotificationBell component is rendered with the correct unseenCount.

                      This should give you a good starting point for testing your Novu component.

                      Additional tests

                      You can also consider a variety of test cases, including rendering tests, interaction tests, and state management tests.

                      Below are some additional tests you could apply:

                      Rendering tests

                      • Ensure the component renders correctly with initial state.
                      • Verify that the notification feed list renders the correct number of notifications.
                      • Check that each notification item displays the expected content (e.g., title, message, timestamp).

                      Interaction tests

                      • Test that clicking on a notification item triggers the appropriate action (e.g., marking as read, opening a detail view).
                      • Ensure the notification bell updates correctly when notifications are read or new notifications arrive.
                      • Verify that the popover opens and closes correctly when the notification bell is clicked.

                      State Management tests

                      • Check that the component correctly handles state changes, such as loading, success, and error states.
                      • Ensure that notifications are correctly added, updated, or removed from the feed.
                      • Verify that unread notifications are accurately counted and displayed.

                      API Integration tests

                      • Mock API responses to ensure the component correctly handles different responses (e.g., successful data fetch, empty list, error response).
                      • Ensure that the component initiates the correct API calls on mount and when interactions occur.

                      Accessibility tests

                      • Verify that the component is accessible, including proper ARIA roles, keyboard navigation, and focus management.

                      Snapshot tests

                      • Create snapshot tests to ensure the UI does not change unexpectedly.

                      Integration with CI/CD

                      Ensure that your tests are integrated with your CI/CD pipeline to automatically run these tests on every push or pull request. This helps catch any issues early and maintains the reliability of your notifications.

                      In case you would like to get even more into how to customize and have a better understanding overall, consider visiting the official documentation.

                       

                      Related Posts

                      category: How to

                      Case Study: How Novu Migrated User Management to Clerk

                      Discover how Novu implemented Clerk for user management, enabling features like SAML SSO, OAuth providers, and multi-factor authentication. Learn about the challenges faced and the innovative solutions developed by our team. This case study provides insights into our process, integration strategies that made it possible.

                      Emil Pearce
                      Emil Pearce
                      category: How to

                      How Product-Development Friction Ruins User Experience with Notifications

                      Discover how friction between product and development teams can lead to poor notifications and damage your user experience. Learn strategies to overcome these challenges and enhance your notification system for better user engagement.

                      KMac Damaso
                      KMac Damaso
                      category: How to

                      How to Grow Engagement By Using Notifications

                      How to grow Product engagement by using Notifications

                      Emil Pearce
                      Emil Pearce