<CourierInbox />and<CourierInboxPopupMenu />, an inbox as a panel or a popup<CourierToast />, toasts for messages that arrive while the app is open<CourierPreferences />, a preference center for topic subscriptionsuseCourier(), the client, for custom UIs and for driving the components from outside
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.
Not using React? Use the
and
packages instead. They provide Web Components for any JavaScript project.
Authentication
Courier authenticates with a JWT that your backend mints with your Courier API key, never in client code. is the full guide: the token flow, scope strings, reading auth state, signing out, token refresh, and EU-hosted workspaces.Quick start
Add the inbox component to your app.signIn:
Component reference
Each component’s props, theme, callbacks, and custom-render surface live with the feature. These links open on the React tab.
The
CourierInboxTheme, CourierToastTheme, and CourierPreferencesTheme objects are the same on React, Vue, Angular, and the Web Components, so each is documented once: , , .
useCourier hook
useCourier() returns the Courier client and the three component surfaces. Use it to build your own UI, or to drive a prebuilt component from outside it.
Components and the hook aren’t a choice between two integrations. The components call the same client the hook returns, so a page can render
<CourierInbox /> and read inbox.totalUnreadCount for a badge elsewhere in the same tree.
Call inbox.listenForUpdates() after you authenticate. It opens the realtime connection, which nothing else in a custom UI calls for you. Without it, the inbox loads once and doesn’t update again.
Advanced
Next.js and SSR
Courier Inbox and Toast support Next.js but render client-side only. In Next.js 13+, add'use client' to the top of any file using Courier components.
Troubleshooting
Inbox not updating in real-time
Inbox not updating in real-time
Call
inbox.listenForUpdates() after authentication. It opens the WebSocket connection required for real-time updates.Messages not loading
Messages not loading
Possible causes:
- Not authenticated: call
signIn() - Feeds not registered: call
registerFeeds()beforeload() - Network errors: check
inbox.errorfor details - JWT expired: generate a new token
Authentication errors
Authentication errors
- Invalid JWT: generate the JWT correctly on your backend
- Expired JWT: JWTs expire. Generate a new one
- Missing scopes: include
inbox:read:messagesandinbox:write:eventsin the JWT - Wrong user ID: the
userIdmust match the user the JWT was issued for
Two tabs show identical messages
Two tabs show identical messages
Two tabs sharing a
datasetId collapse into one dataset. Every datasetId has to be unique across all feeds, not only within one. See .TypeScript errors
TypeScript errors
Import types directly from the package:
React 17 vs React 18 package mismatch
React 17 vs React 18 package mismatch
Use 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'), and include only the scopes you need. - Performance:
canUseCache: true(default) serves cached data. UsedatasetIdsto load only the datasets you need, and set a suitablesetPaginationLimit(). The hook’s reactive state updates on its own, so avoid unnecessary re-renders. - Testing: mock
useCourier()in tests to return test data:
EU and regional endpoints
Only needed if your workspace uses the .@trycourier/courier-react re-exports EU_COURIER_API_URLS, DEFAULT_COURIER_API_URLS, and getCourierApiUrlsForRegion from @trycourier/courier-js. Pass the result as apiUrls on sign-in. For the hostnames, the helper semantics, and the same-region JWT requirement, see .