Embed a customizable in-app notification center and toasts in your React app. Courier React components seamlessly integrate with email, SMS, push, and more.
The Courier React SDK provides ready-made components and programmatic hooks for building notification experiences:
<CourierInbox /> — full-featured inbox for displaying and managing messages
<CourierInboxPopupMenu /> — popup menu version of the inbox
<CourierToast /> — toast notifications for time-sensitive alerts
useCourier() — hook for programmatic access and custom UIs
Available on
GitHub
and npm.Courier publishes two React packages: @trycourier/courier-react for React 18+ and @trycourier/courier-react-17 for React 17.
Copy
Ask AI
npm install @trycourier/courier-react
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.
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.
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).
Get up and running with Courier React in minutes. This minimal example shows how to add the inbox component to your app.
Copy
Ask AI
"use client"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 courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: jwt, }); }, []); return <CourierInbox />;}
Tabs and FeedsTabs and feeds organize and filter messages in the inbox. A feed is a container that groups related tabs together. Each tab applies filters to show relevant messages.
If there is only one feed, the feed selection dropdown is hidden. If a feed has only one tab, the tab bar is hidden and the unread count appears next to the feed.
Filter options for each tab:
Filter Property
Type
Description
tags
string[]
Messages that have any of the specified tags
archived
boolean
Whether to include archived messages (defaults to false if unset)
onMessageLongPress is only applicable on devices that support touch events.
Copy
Ask AI
import { useEffect } from "react";import { CourierInbox, useCourier, type CourierInboxListItemFactoryProps, type CourierInboxListItemActionFactoryProps} from "@trycourier/courier-react";export default function App() { const courier = useCourier(); useEffect(() => { const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; courier.shared.signIn({ userId: $YOUR_USER_ID, 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(`Action clicked: ${JSON.stringify(action, null, 2)}`); }} onMessageLongPress={({ message, index }: CourierInboxListItemFactoryProps) => { alert(`Message long pressed at index ${index}`); }} /> );}
Styles and ThemingCustomize the inbox to match your app with a theme object. You can customize fonts, icons, text, and more. Check out the CourierInboxTheme reference.
Theme utilities: defaultLightTheme / defaultDarkTheme provide the default inbox themes, and mergeTheme(baseTheme, overrideTheme) merges two themes with the override taking precedence.
You can also use React refs (CourierInboxElement, CourierInboxPopupMenuElement) for programmatic access to component methods like removeHeader().
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.
Toasts are short-lived notifications that notify users and prompt them to take action.
The 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.
Some initialization for toasts is asynchronous. If your app displays toasts immediately when
the component is mounted, consider using the onReady callback (see props table below) to wait
until the component is fully initialized.
Handle Clicks
Callback Prop
Type Signature
onToastItemClick
(props: CourierToastItemClickEvent) => void
onToastItemActionClick
(props: CourierToastItemActionClickEvent) => void
If a message contains actions, toast items
include a button for each. Use onToastItemActionClick to handle those clicks.
Dismiss button visibility. "auto" shows always if autoDismiss is false, on hover if true.
onToastItemClick
fn
undefined
Callback when a toast is clicked.
onToastItemActionClick
fn
undefined
Callback when a toast action button is clicked.
renderToastItem
fn
undefined
Custom render for entire toast items.
renderToastItemContent
fn
undefined
Custom render for toast item content only.
onReady
(ready: boolean) => void
undefined
Callback when the component is ready to receive messages.
Enabling autoDismiss adds a countdown bar to each toast and automatically removes it after the timeout. The countdown bar color is theme-able via autoDismissBarColor in CourierToastTheme.
Using onReady: If toasts display immediately on mount or custom render functions don’t apply correctly, use onReady to wait for full initialization before authenticating:
The useCourier() hook provides programmatic access to Courier functionality for building custom UIs or integrating Courier features into existing components.When to use hooks vs components:
Components (<CourierInbox />, <CourierToast />): Quick integration with default UI
Hooks (useCourier()): Custom UIs, programmatic control, advanced state management
Both together: Use hooks for state management while components handle rendering
Complete example — authentication, inbox setup, real-time updates, and displaying messages:
You must call inbox.listenForUpdates() after authentication to enable real-time message updates. Without this, the inbox only shows messages from the initial load.
Next.js and SSRCourier Inbox and Toast support Next.js but only render client-side. In Next.js 13+, add 'use client' to the top of any file using Courier components.
Copy
Ask AI
"use client"import { CourierInbox } from "@trycourier/courier-react";export default function Page() { // Authentication code... return <CourierInbox />;}
Troubleshooting
Inbox not updating in real-time
Make sure you’ve called inbox.listenForUpdates() after authentication. This establishes the WebSocket connection required for real-time updates.
Not authenticated — ensure signIn() has been called
Feeds not registered — call registerFeeds() before load()
Network errors — check inbox.error for details
JWT expired — generate a new token
Authentication errors
Invalid JWT: Ensure the JWT is generated correctly on your backend
Expired JWT: JWTs have an expiration time; generate a new one
Missing scopes: Ensure your JWT includes inbox:read:messages and inbox:write:events
Wrong user ID: Verify the userId matches the user the JWT was issued for
TypeScript errors
Import types directly from the package:
Copy
Ask AI
import { useCourier, type InboxMessage, type CourierInboxFeed } from '@trycourier/courier-react';
React 17 vs React 18 package mismatch
Ensure you’re using the correct package:
React 18+: @trycourier/courier-react
React 17: @trycourier/courier-react-17
Best Practices
JWT Security: Always generate JWTs server-side. Cache on the client, refresh before expiration (standard: '1d'), include only necessary scopes.
Performance: Use canUseCache: true (default) for cached data. Use datasetIds to load only needed datasets. Set appropriate setPaginationLimit() values. The hook’s reactive state updates automatically; avoid triggering unnecessary re-renders.
Testing: Mock useCourier() in tests to return test data:
All filter properties are AND’d together. For example, { tags: ['important'], status: 'unread' } shows messages that have the ‘important’ tag AND are unread.