Skip to main content
Courier React Inbox
This is the latest version of the Courier React SDK, recommended for new and existing apps.If you’re coming from an earlier version of the Courier React SDK, check out the v8 migration guide for what’s changed, how to upgrade your app, and links to documentation for past versions of the React SDK.

Installation

Available on GitHub and npm. Courier publishes two React packages: @trycourier/courier-react for React 18+ and @trycourier/courier-react-17 for React 17.
npm install @trycourier/courier-react
Not using React? Check out the @trycourier/courier-ui-inbox and @trycourier/courier-ui-toast packages instead, which provide Web Components for any JavaScript project.

Authentication

To use the SDK, you need to generate a JWT (JSON Web Token) for your user. This JWT should always be generated by your backend server, never in client-side code.

JWT Authentication Flow

1

Your client calls your backend

When your app needs to authenticate a user, your client should make a request to your own backend (ex. GET https://your-awesome-app.com/api/generate-courier-jwt).
2

Your backend calls Courier

In your backend endpoint, use your Courier API Key to call the Courier Issue Token Endpoint and generate a JWT for the user.
3

Your backend returns the JWT to your client

Having received the JWT from Courier, your backend should return it to your client and pass it to the Courier SDK.
See all available user scopes for the Courier APIs.

Development Authentication with cURL

To quickly test JWT generation for development only, you can use cURL to call the Courier Issue Token Endpoint directly.
Do not call the Issue Token API from client-side code. Always keep your Courier API keys secure.
curl --request POST \
     --url https://api.courier.com/auth/issue-token \
     --header 'Accept: application/json' \
     --header 'Authorization: Bearer $YOUR_API_KEY' \
     --header 'Content-Type: application/json' \
     --data \
 '{
    "scope": "user_id:$YOUR_USER_ID write:user-tokens inbox:read:messages inbox:write:events read:preferences write:preferences read:brands",
    "expires_in": "$YOUR_NUMBER days"
  }'

Components

This guide covers two Courier React components that can be used together or independently:
  • Courier Inbox - A persistent notification center for displaying and managing all user messages. Available as a full inbox view or popup menu.
  • Courier Toast - Temporary toast notifications for time-sensitive alerts and updates.
Both components share the same authentication mechanism and connect to the same message feed.

Adding the Inbox Component

<CourierInbox />

Preview of the default CourierInbox component

Default CourierInbox component

import { useEffect } from "react";
import { CourierInbox, useCourier } from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user with the inbox
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  return <CourierInbox />;
}

<CourierInboxPopupMenu />

Preview of the default CourierInboxPopupMenu component

Default CourierInboxPopupMenu component

import { useEffect } from "react";
import { CourierInbox, useCourier } from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user with the inbox
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  return (
    <div style={{ padding: "24px" }}>
      <CourierInboxPopupMenu />
    </div>
  );
}
Looking for toast notifications? See the Toast Component section below.

Handle Clicks and Presses

Pass callback functions to the onMessageClick, onMessageActionClick, and onMessageLongPress props to handle clicks and presses in the inbox.
Callback PropType Signature
onMessageClick(props: CourierInboxListItemFactoryProps) => void
onMessageActionClick(props: CourierInboxListItemActionFactoryProps) => void
onMessageLongPress(props: CourierInboxListItemFactoryProps) => void
onMessageLongPress is only applicable on devices that support touch events.
import { useEffect } from "react";
import {
  CourierInbox,
  useCourier,
  type CourierInboxListItemFactoryProps,
  type CourierInboxListItemActionFactoryProps
} from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user with the inbox
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  return (
    <CourierInbox
      onMessageClick={(
        { message, index }: CourierInboxListItemFactoryProps) => {
        alert(`Message clicked at index ${index}:\n` +
              `${JSON.stringify(message, null, 2)}`);
      }}

      onMessageActionClick={(
        { message, action, index }: CourierInboxListItemActionFactoryProps) => {
        alert(`Message action clicked at index ${index}:\n` +
              `Action: ${JSON.stringify(action, null, 2)}\n` +
              `Message: ${JSON.stringify(message, null, 2)}`);
      }}

      onMessageLongPress={(
        { message, index }: CourierInboxListItemFactoryProps) => {
        alert("Message long pressed at index " +
              `${index}:\n${JSON.stringify(message, null, 2)}`);
      }}
    />
  );

}

Styles and Theming

The fastest way to style the Courier Inbox to match your app is with a custom theme.
You can customize fonts, icons, text, and more. Check out the CourierInboxTheme reference.

Light and Dark Themes

Customize both the light and dark themes. The example below shows unread indicator styling applied in both cases.
Courier React Inbox with a custom unread indicator style

Courier React Inbox with a custom unread indicator style

import {
  CourierInbox,
  type CourierInboxTheme
} from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  const theme: CourierInboxTheme = {
    inbox: {
      header: {
        filters: {
          unreadIndicator: {
            backgroundColor: "#8B5CF6",
          },
        },
      },
      list: {
        item: {
          unreadIndicatorColor: "#8B5CF6",
        },
      },
    },
  };

  return <CourierInbox lightTheme={theme} darkTheme={theme} mode="light" />;
}
Customizing the popup menu's alignment, position, and dimensions

Customizing the popup menu's alignment, position, and dimensions

Customize the popup menu’s alignment with respect to its button. Available options for CourierInboxPopupAlignment are:
Vertical AlignmentOptions
Top"top-right", "top-center", "top-left"
Center"center-right", "center-center", "center-left"
Bottom"bottom-right", "bottom-center", "bottom-left"
import { CourierInboxPopupMenu } from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  return (
    <div style={{ display: "flex", justifyContent: "center", alignItems: "center", padding: "100px" }}>
      <CourierInboxPopupMenu
        popupAlignment="top-left"
        popupWidth="340px"
        popupHeight="400px"
        top="44px"
        left="44px"
      />
    </div>
  );

}

Height

<CourierInbox /> has a default height of auto, meaning it will set its height based on its children. Give the inbox a fixed height by setting the height prop.
import { CourierInbox } from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  return <CourierInbox height="50vh" />;
}

Custom Elements

List Items

Render PropType Signature
renderListItem(props: CourierInboxListItemFactoryProps) => ReactNode
Customize the inbox message list item by passing a React component to the renderListItem prop.
Custom inbox message list item displaying the message object

Custom inbox message list item displaying the message object

import {
  CourierInbox,
  type CourierInboxListItemFactoryProps
} from "@trycourier/courier-react"

const CustomListItem = (
  { message, index }: CourierInboxListItemFactoryProps) => (
    <pre style={{
      padding: "24px",
      borderBottom: "1px solid #e0e0e0",
      margin: "0"
    }}>
      {JSON.stringify({ message, index }, null, 2)}
    </pre>
  );

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierInbox
      renderListItem={(props: CourierInboxListItemFactoryProps) => {
        return <CustomListItem {...props} />
      }}
    />
  );
}

Custom Header

Render PropType Signature
renderHeader(props: CourierInboxHeaderFactoryProps) => ReactNode
Customize the inbox header by passing a React component to the renderHeader prop.
Custom inbox header

Custom inbox header

import {
  CourierInbox,
  type CourierInboxHeaderFactoryProps
} from "@trycourier/courier-react";

const CustomHeader = (props: CourierInboxHeaderFactoryProps) => (
  <div style={{
    background: "red",
    fontSize: "24px",
    padding: "24px",
    width: "100%"
  }}>
    {props.feedType}
  </div>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierInbox
      renderHeader={(props: CourierInboxHeaderFactoryProps) => {
        return <CustomHeader {...props} />
      }}
    />
  );
}
Render PropType Signature
renderMenuButton(props: CourierInboxMenuButtonFactoryProps) => ReactNode
Customize the inbox popup menu button by passing a React component to the renderMenuButton prop.
Custom inbox popup menu button

Custom inbox popup menu button

import {
  CourierInboxPopupMenu,
  type CourierInboxMenuButtonFactoryProps
} from "@trycourier/courier-react"

const CustomMenuButton = ({ unreadCount }: CourierInboxMenuButtonFactoryProps) => (
  <button>
    Open the Inbox Popup. Unread message count: {unreadCount}
  </button>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <div style={{ padding: "24px" }}>
      <CourierInboxPopupMenu
        renderMenuButton={(props: CourierInboxMenuButtonFactoryProps) => {
          return <CustomMenuButton {...props} />
        }}
      />
    </div>
  );
}

Loading, Empty, Error States and Custom Pagination

Render PropType Signature
renderLoadingState(props: CourierInboxStateLoadingFactoryProps) => ReactNode
renderEmptyState(props: CourierInboxStateEmptyFactoryProps) => ReactNode
renderErrorState(props: CourierInboxStateErrorFactoryProps) => ReactNode
renderPaginationItem(props: CourierInboxPaginationItemFactoryProps) => ReactNode
Customize the inbox loading, empty, error states and pagination item by passing a React component to the renderLoadingState, renderEmptyState, renderErrorState, and renderPaginationItem props.
Subsequent pages of messages are loaded automatically when the user scrolls to the bottom of the inbox, so the pagination component may only be visible briefly.
Custom pagination state

Custom pagination state

import {
  CourierInbox,
  type CourierInboxStateEmptyFactoryProps,
  type CourierInboxStateLoadingFactoryProps,
  type CourierInboxStateErrorFactoryProps,
  type CourierInboxPaginationItemFactoryProps
} from "@trycourier/courier-react"

const CustomLoadingState = ({ feedType }: CourierInboxStateLoadingFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "red",
    textAlign: "center"
  }}>
    Custom Loading State
  </div>
);

const CustomEmptyState = ({ feedType }: CourierInboxStateEmptyFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "green",
    textAlign: "center"
  }}>
    Custom Empty State
  </div>
);

const CustomErrorState = ({ feedType, error }: CourierInboxStateErrorFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "blue",
    textAlign: "center"
  }}>
    Custom Error State: {error.message}
  </div>
);

const CustomPaginationItem = ({ feedType }: CourierInboxPaginationItemFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "#f0f0f0",
    textAlign: "center"
  }}>
    🔄 Loading the next page of <b>{feedType}</b> messages
  </div>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierInbox
      renderLoadingState={(props: CourierInboxStateLoadingFactoryProps) => {
        return <CustomLoadingState {...props} />
      }}
      renderEmptyState={(props: CourierInboxStateEmptyFactoryProps) => {
        return <CustomEmptyState {...props} />
      }}
      renderErrorState={(props: CourierInboxStateErrorFactoryProps) => {
        return <CustomErrorState {...props} />
      }}
      renderPaginationItem={(props: CourierInboxPaginationItemFactoryProps) => {
        return <CustomPaginationItem {...props} />
      }}
    />
  );

}

Adding the Toast Component

<CourierToast />

Courier Toast component

Toasts are short-lived notifications that notify users and prompt them to take action. Courier’s Toast component is connected to the feed of Courier Inbox messages.
Toasts are synced with the Inbox message feed. You can use both components together to provide persistent and temporary notifications in your app. See the Inbox Component section above.
Add <CourierToast /> to your app and authenticate the user with the Courier backend:
import { useEffect } from "react";
import { CourierToast, useCourier } from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  return <CourierToast />;
}
Some initialization for toasts is asychronous. If your app displays toasts immediately when the component is mounted, consider using the onReady to wait until the <CourierToast> component is fully initialized.

Handle Clicks

Toast Items

Callback PropType Signature
onToastItemClick(props: CourierToastItemClickEvent) => void
Pass a callback function to the onToastItemClick prop to handle clicks on toast items.
import { useEffect } from "react";
import {
  CourierToast,
  useCourier,
  type CourierToastItemClickEvent
} from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user
    courier.shared.signIn({ userId, jwt });
  }, []);

  return (
    <CourierToast
      onToastItemClick={({ message }: CourierToastItemClickEvent) => {
        console.log("Toast clicked:", message);
      }},
    />
  );
}

Action Buttons

Callback PropType Signature
onToastItemActionClick(props: CourierToastItemActionClickEvent) => void

Courier Toast with action buttons

If a message contains actions, toast items will include a button for each. By default, these buttons do not implement any functionality. Use onToastItemActionClick to handle clicks on action buttons within toast items.
import { useEffect } from "react";
import {
  CourierToast,
  useCourier,
  type CourierToastItemActionClickEvent
} from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    // Generate a JWT for your user on your backend server
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    // Authenticate the user
    courier.shared.signIn({ userId, jwt });
  }, []);

  return (
    <CourierToast
      onToastItemActionClick={({ message, action }: CourierToastItemActionClickEvent) => {
        window.open(action.href);
      }}
    />
  );
}

Styles and Theming

The fastest way to style Courier Toast to match your app is with a custom theme.
You can customize fonts, icons, colors, and more. The full CourierToastTheme reference is included below.

Light and Dark Themes

Customize both the light and dark themes. The example below shows custom styling applied to toast items.

Courier Toast with a custom theme

import {
  CourierToast,
  type CourierToastTheme
} from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  const theme: CourierToastTheme = {
    toast: {
      item: {
        title: {
          color: "#6366f1",
          weight: "bold",
        },
        backgroundColor: "#edeefc",
        border: "1px solid #cdd1ff",
        borderRadius: "15px",
      },
    },
  };

  return <CourierToast lightTheme={theme} mode="light" />;
}

CourierToastTheme

An instance of CourierToastTheme can be set as the light and dark themes respectively.

Custom Elements

Custom Content

Toast with custom item content

Render PropType Signature
renderToastItemContent(props: CourierToastItemFactoryProps) => ReactNode
Customize only the content area of each toast item while reusing the default stack structure and styling by passing a factory function to renderToastItemContent. The dismiss button and auto-dismiss functionality are still handled by the default toast item wrapper.
import {
  CourierToast,
  type CourierToastItemFactoryProps
} from "@trycourier/courier-react";

const CustomToastContent = ({ message }: CourierToastItemFactoryProps) => (
  <div style={{ padding: "16px" }}>
    <strong style={{ display: "block", marginBottom: "4px" }}>
      {message.title}
    </strong>
    <p style={{ margin: 0, fontSize: "14px", color: "#6b7280" }}>
      {message.body}
    </p>
  </div>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierToast
      renderToastItemContent={(props: CourierToastItemFactoryProps) => {
        return <CustomToastContent {...props} />
      }}
    />
  );
}

Fully Custom Items

Render PropType Signature
renderToastItem(props: CourierToastItemFactoryProps) => ReactNode
Customize each toast item’s appearance and behavior completely by passing a factory function to renderToastItem. This gives you complete control over the toast item, including its container, styling, and dismiss behavior. When using renderToastItem, the autoDismiss and autoDismissTimeoutMs props are still respected, and toasts will be automatically removed after the timeout if autoDismiss is enabled.

Toast with a fully custom item

import {
  CourierToast,
  type CourierToastItemFactoryProps
} from "@trycourier/courier-react";

const CustomToastItem = ({ message, autoDismiss, autoDismissTimeoutMs, dismiss }: CourierToastItemFactoryProps) => (
  <div style={{
    display: "flex",
    alignItems: "center",
    gap: "12px",
    marginBottom: "8px"
  }}>
    <div style={{
      flex: 1,
      padding: "16px",
      background: "#f6f6fe",
      border: "1px solid #c6c2ff",
      borderRadius: "8px",
    }}>
      <strong style={{ display: "block", marginBottom: "4px" }}>
        {message.title}
      </strong>
      <p style={{ margin: 0, fontSize: "14px" }}>
        {message.body}
      </p>
    </div>

    <div style={{
      display: "flex",
      flexDirection: "column",
      gap: "8px",
      minWidth: "100px"
    }}>
      {message.actions?.map((action, index) => (
        <button
          key={index}
          onClick={() => window.open(action.href)}
          style={{
            padding: "8px 12px",
            background: "#f6f6fe",
            border: "1px solid #c6c2ff",
            borderRadius: "8px",
          }}
        >
          {action.content}
        </button>
      ))}
    </div>
  </div>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierToast
      renderToastItem={(props: CourierToastItemFactoryProps) => {
        return <CustomToastItem {...props} />
      }}
    />
  );
}

CourierToast Props

The full set of React props you can pass to the <CourierToast> component.
Prop NameTypeDefaultDescription
styleCSSProperties{ position: "fixed", width: "380px", top: "30px", right: "30px", zIndex: 999 }Styles applied to the toast component. Useful for customizing position and layout.
lightThemeCourierToastThemeundefinedTheme object used when light mode is active.
darkThemeCourierToastThemeundefinedTheme object used when dark mode is active.
mode"light" | "dark" | "system""system"Manually set the theme mode.
autoDismissbooleanfalseEnable toasts to auto-dismiss with a countdown bar.
autoDismissTimeoutMsnumber5000Timeout in milliseconds before a toast auto-dismisses.
dismissButton"visible" | "hidden" | "hover" | "auto""auto"Set dismiss button visibility. "auto" shows button always if autoDismiss is false, on hover if true.
onToastItemClick(props: CourierToastItemClickEvent) => voidundefinedCallback invoked when a toast item is clicked.
onToastItemActionClick(props: CourierToastItemActionClickEvent) => voidundefinedCallback invoked when a toast item action button is clicked.
renderToastItem(props: CourierToastItemFactoryProps) => ReactNodeundefinedCustom render function for entire toast items.
renderToastItemContent(props: CourierToastItemFactoryProps) => ReactNodeundefinedCustom render function for toast item content only.
onReady(ready: boolean) => voidundefinedCallback invoked when the component is ready to receive messages.

Using auto-dismiss

Toast component with auto-dismiss enabled

Use auto-dismissed toasts for non-critical notifications that don’t require immediate action. For example, you might use an auto-dismissed toast to notify a user that a scheduled task completed without issue. Auto-dismiss details Enabling auto-dismiss changes a few bits of toasts’ default behavior:
  • Toast items are automatically removed after the specified timeout.
  • A countdown bar appears at the top of each toast to indicate the time remaining before dismissal. This countdown bar is theme-able via the autoDismissBarColor value in CourierToastTheme.
  • The dismiss button (x) is only visible on hover, but you can customize this behavior using the dismissButton prop.
import { CourierToast } from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierToast
      autoDismiss={true}
      autoDismissTimeoutMs={7000}
    />
  );
}

Using the onReady Callback

The onReady callback is invoked when the CourierToast component has finished mounting and is ready to display messages. When to use onReady Some initialization for <CourierToast> is asychronous, such as syncing state between React and the underlying Web Components. If you’re displaying toasts immediately on page load (for example, with CourierToastDatastore) or observing issues where toasts do not properly render custom content or click handlers, consider using onReady to wait for <CourierToast> to fully initialize before acting. In the example below, onReady is used to toggle the toastReady state, passed as a dependency to the React effect that performs authentication.
import { useEffect, useState } from "react";
import {
  CourierToast,
  useCourier,
  type CourierToastItemFactoryProps
} from "@trycourier/courier-react";

const CustomToastItem = ({ message }: CourierToastItemFactoryProps) => (
  <div style={{ padding: "16px", background: "#f3f4f6", borderRadius: "8px" }}>
    <strong>{message.title}</strong>
  </div>
);

export default function App() {
  const [toastReady, setToastReady] = useState(false);
  const courier = useCourier();

  useEffect(() => {
    if (toastReady) {
      // Only authenticate after the toast is ready to ensure
      // custom render functions are applied
      const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

      courier.shared.signIn({
        userId: $YOUR_USER_ID,
        jwt: jwt,
      });
    }
  }, [toastReady, courier]);

  return (
    <CourierToast
      onReady={setToastReady}
      renderToastItem={(props: CourierToastItemFactoryProps) => {
        return <CustomToastItem {...props} />
      }}
    />
  );
}

Next.js and Server Side Rendering Frameworks

Both Courier Inbox and Courier Toast support Next.js, however the components only support client side rendering. In Next.js version 13+, make sure to add the 'use client' directive to the top of any file that uses Courier components.
"use client"

import { CourierInbox } from "@trycourier/courier-react";

export default function Page() {
  // Courier authentication and other component code...

  return <CourierInbox />;
}