Embed a customizable in-app notification center in your web app. Courier Inbox seamlessly integrates with email, SMS, push, and more.
This is the latest version of the Courier Web Components SDK, recommended for new and existing apps.Coming from an earlier version? We recommend upgrading — check out the
migration guide for the React SDK, which
is a thin wrapper around Inbox Web Components and exposes a similar API.
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.
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).
Importing the Courier SDK registers Courier’s Web Components (<courier-inbox>, <courier-inbox-popup-menu>).
Copy
Ask AI
<body> <courier-inbox id="inbox"></courier-inbox> <script type="module"> import { Courier } from '@trycourier/courier-ui-inbox'; // 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 }); </script></body>
Importing the Courier SDK registers Courier’s Web Components (<courier-inbox>, <courier-inbox-popup-menu>).
Copy
Ask AI
<body> <div style="padding: 24px;"> <courier-inbox-popup-menu id="inbox"></courier-inbox-popup-menu> </div> <script type="module"> import { Courier } from '@trycourier/courier-ui-inbox'; // 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 }); </script></body>
Tabs and feeds allow you to organize and filter messages in your inbox. A feed is a container that groups related tabs together. Each tab represents a filtered view of messages.
Feed: A top-level category that appears in the inbox header. Each feed can contain multiple tabs. For example, a social media app might have feeds like “All”, “Jobs”, “My Posts”, and “Mentions”.
Tab: A filtered view within a feed that applies specific filters to show relevant messages. For example, a “Notifications” feed might have tabs like “All”, “Unread”, and “Important”.
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.
Each tab uses a filter object to determine which messages to display. Filters are combined with AND logic, meaning all specified conditions must be met.
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)
Use the onMessageClick(), onMessageActionClick(), and onMessageLongPress() methods to handle clicks and presses in the inbox.
onMessageLongPress() is only applicable on devices that support touch events.
Copy
Ask AI
<body> <courier-inbox id="inbox"></courier-inbox> <!-- Uncomment the line below to use the popup menu instead --> <!-- <courier-inbox-popup-menu id="inbox"></courier-inbox-popup-menu> --> <script type="module"> import { Courier } from '@trycourier/courier-ui-inbox'; // 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 }); const inbox = document.getElementById('inbox'); // Handle message clicks inbox.onMessageClick(({ message, index }) => { alert("Message clicked at index " + index + ":\n" + JSON.stringify(message, null, 2)); }); // Handle message action clicks (these are buttons on individual messages) inbox.onMessageActionClick(({ message, action, index }) => { alert( "Message action clicked at index " + index + ":\n" + "Action: " + JSON.stringify(action, null, 2) + "\n" + "Message: " + JSON.stringify(message, null, 2) ); }); // Handle message long presses (useful for mobile web) inbox.onMessageLongPress(({ message, index }) => { alert("Message long pressed at index " + index + ":\n" + JSON.stringify(message, null, 2)); }); </script></body>
<courier-inbox> 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 attribute.
Customize the inbox header by passing an HTML element to the setHeader method
on the <courier-inbox> component.Call removeHeader to remove the header entirely.The setHeader callback receives a props object with a feeds array. Each feed includes selection state, tabs with unread counts, and filter information.
Copy
Ask AI
<body> <courier-inbox id="inbox"></courier-inbox> <script type="module"> import { Courier } from '@trycourier/courier-ui-inbox'; // Reference the courier-inbox element const inbox = document.getElementById('inbox'); // Set a custom header that displays feed and tab information inbox.setHeader((props) => { const headerDiv = document.createElement('div'); headerDiv.style.background = 'red'; headerDiv.style.fontSize = '20px'; headerDiv.style.color = 'white'; headerDiv.style.padding = '20px'; headerDiv.style.width = '100%'; headerDiv.style.fontFamily = 'monospace'; // Find selected feed and tab const selectedFeed = props.feeds.find(feed => feed.isSelected); const selectedTab = selectedFeed?.tabs.find(tab => tab.isSelected); // Prepare summary for header let feedPart = selectedFeed ? `${selectedFeed.title}` : 'No Feed Selected'; let tabPart = selectedTab ? `${selectedTab.title}` : 'No Tab Selected'; let unreadPart = typeof selectedTab?.unreadCount === 'number' ? `Unread: ${selectedTab.unreadCount}` : ''; headerDiv.textContent = `${feedPart} / ${tabPart} ${unreadPart && '- ' + unreadPart}`; return headerDiv; }); // Set the feeds inbox.setFeeds([ { feedId: 'inbox', title: 'Inbox', tabs: [ { datasetId: 'inbox', title: 'Inbox', filter: {} } ] }, { feedId: 'archive', title: 'Archive', tabs: [ { datasetId: 'archive_1', title: 'Archive 1', filter: { archived: true } }, { datasetId: 'archive_2', title: 'Archive 2', filter: { archived: true } } ] } ]); // Authenticate the user Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' }); // Uncomment the line below to remove the header // inbox.removeHeader(); </script></body>
Show Header Factory Props Structure
The setHeader callback receives a props object with the following structure:
Loading, Empty, Error States and Custom Pagination
Customize the inbox loading, empty, error states and pagination item by passing an
HTML element to the setLoadingState, setEmptyState, setErrorState, and
setPaginationItem methods.
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.
Copy
Ask AI
<body> <courier-inbox id="inbox"></courier-inbox> <script type="module"> // Reference the courier-inbox element const inbox = document.getElementById('inbox'); // Set a custom loading state inbox.setLoadingState(props => { const loading = document.createElement('div'); loading.style.padding = '24px'; loading.style.background = 'red'; loading.textContent = 'Custom Loading State'; return loading; }); // Set a custom empty state inbox.setEmptyState(props => { const empty = document.createElement('div'); empty.style.padding = '24px'; empty.style.background = 'green'; empty.textContent = 'Custom Empty State'; return empty; }); // Set a custom error state inbox.setErrorState(props => { const error = document.createElement('div'); error.style.padding = '24px'; error.style.background = 'blue'; error.textContent = 'Custom Error State'; return error; }); // Set a custom pagination state inbox.setPaginationItem(props => { const pagination = document.createElement('div'); pagination.style.padding = '24px'; pagination.style.background = '#f0f0f0'; pagination.style.textAlign = 'center'; pagination.textContent = '🔄 Loading the next page of messages'; return pagination; }); </script></body>
Switches to the specified tab within the current feed and loads its data.
Copy
Ask AI
<script type="module"> import { Courier } from '@trycourier/courier-ui-inbox'; const inbox = document.getElementById('inbox'); Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: '...' }); // Switch to the "Unread" tab in the current feed inbox.selectTab('unread');</script>
Parameters:
tabId (string) - The ID of the tab (datasetId) to select
Throws: Error if tab doesn’t exist in the current feed
Many inbox configuration options can be set either via HTML attributes or programmatic methods. Choose the approach that best fits your use case:Use HTML attributes when:
Setting initial configuration that doesn’t change
Configuring the inbox declaratively in your HTML
You prefer a simpler, more declarative approach
Use programmatic methods when:
Configuration needs to change dynamically at runtime
You need to respond to user interactions or application state
You’re building dynamic UIs that require runtime updates
You need to access current state (getters) or perform actions (methods)
Example: Both approaches achieve the same result
Copy
Ask AI
<!-- Using HTML attributes --><courier-inbox mode="light" light-theme='{"inbox": {"backgroundColor": "#fff"}}'></courier-inbox><!-- Using programmatic methods --><courier-inbox id="inbox"></courier-inbox><script type="module"> const inbox = document.getElementById('inbox'); inbox.setMode('light'); inbox.setLightTheme({ inbox: { backgroundColor: '#fff' } });</script>
Note: Some features are only available via methods (e.g., selectFeed(), refresh(), getFeeds()) and cannot be set via attributes.