
<CourierInbox />— full-featured inbox for displaying and managing messages<CourierInboxPopupMenu />— popup menu version of the inbox<CourierToast />— toast notifications for time-sensitive alerts<CourierPreferences />— notification preferences center for managing topic subscriptions and deliveryuseCourier()— composable for programmatic access and custom UIs
Installation
Available on GitHub and npm.vue (>= 3.3) is a peer dependency.
Not using Vue? 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.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).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.
For a step-by-step walkthrough of authentication and token generation, see our JWT authentication tutorial.
Development testing with cURL
To quickly test JWT generation for development only, you can call the Issue Token Endpoint directly.Quick Start
Get up and running with Courier Vue in minutes. This minimal example shows how to add the inbox component to your app. Courier components are normal Vue components — import them and use them in your template.Inbox Component
<CourierInbox />
If you’re using tenants, scope requests to a particular
tenant by passing its ID to For the full reference of sign in parameters, see the Courier JS docs.
signIn:<CourierInboxPopupMenu />
Tabs and Feeds
Tabs 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. Pass them with the:feeds prop.
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 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) |
status | 'read' | 'unread' | Filter by read/unread status |
Handle Clicks and Presses
Listen for interactions with@message-click, @message-action-click, and @message-long-press.
| Event | Payload |
|---|---|
@message-click | CourierInboxListItemFactoryProps |
@message-action-click | CourierInboxListItemActionFactoryProps |
@message-long-press | CourierInboxListItemFactoryProps |
Styles and Theming
Customize the inbox to match your app with a theme object passed to:light-theme and/or :dark-theme. Pass the object directly — the component serializes it for you.
defaultLightTheme / defaultDarkTheme provide the default inbox themes, and mergeTheme(baseTheme, overrideTheme) merges two themes with the override taking precedence.
The full CourierInboxTheme type covers the popup trigger button, the inbox window (header, feeds, tabs, actions), the message list (items, scrollbar, menus), and loading/empty/error states. Every property is optional. See the complete reference in the Courier React SDK theme reference (the theme object is identical across SDKs).
Popup alignment and dimensions
| Vertical Alignment | Options |
|---|---|
| Top | "top-right", "top-center", "top-left" |
| Center | "center-right", "center-center", "center-left" |
| Bottom | "bottom-right", "bottom-center", "bottom-left" |
Fixed height
<CourierInbox /> has a default height of auto. Set a fixed height with the height prop:
Custom Elements
You can customize individual parts of the inbox by passing render props — functions that receive typed props and return a Vue node built withh(). Bind them with :render-*.
| Render Prop | Type Signature |
|---|---|
:render-list-item | (props: CourierInboxListItemFactoryProps) => VNodeChild |
:render-header | (props: CourierInboxHeaderFactoryProps) => VNodeChild |
:render-menu-button | (props: CourierInboxMenuButtonFactoryProps) => VNodeChild (popup menu) |
:render-loading-state | (props: CourierInboxStateLoadingFactoryProps) => VNodeChild |
:render-empty-state | (props: CourierInboxStateEmptyFactoryProps) => VNodeChild |
:render-error-state | (props: CourierInboxStateErrorFactoryProps) => VNodeChild |
:render-pagination-item | (props: CourierInboxPaginationItemFactoryProps) => VNodeChild |
removeHeader()) via a template ref — the component exposes getElement():
Toast Component
<CourierToast />
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.
Some initialization for toasts is asynchronous. If your app displays toasts immediately when
the component is mounted, use the
@ready event to wait until the component is fully initialized.Handle Clicks
| Event | Payload |
|---|---|
@toast-item-click | CourierToastItemClickEvent |
@toast-item-action-click | CourierToastItemActionClickEvent |
Styles and Theming
defaultToastLightTheme / defaultToastDarkTheme for defaults, and mergeToastTheme(baseTheme, overrideTheme) to merge.
Custom Elements
| Render Prop | Description |
|---|---|
:render-toast-item-content | (props: CourierToastItemFactoryProps) => VNodeChild — customize the content area only |
:render-toast-item | (props: CourierToastItemFactoryProps) => VNodeChild — fully replace each toast item |
CourierToast Props
| Prop Name | Type | Default | Description |
|---|---|---|---|
style | StyleValue | { position: "fixed", width: "380px", top: "30px", right: "30px", zIndex: 999 } | Styles applied to the toast component. |
lightTheme | CourierToastTheme | undefined | Theme for light mode. |
darkTheme | CourierToastTheme | undefined | Theme for dark mode. |
mode | "light" | "dark" | "system" | "system" | Theme mode. |
autoDismiss | boolean | false | Enable auto-dismiss with countdown bar. |
autoDismissTimeoutMs | number | 5000 | Timeout in ms before auto-dismiss. |
dismissButton | "visible" | "hidden" | "hover" | "auto" | "auto" | Dismiss button visibility. |
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. |
Preferences Component
<CourierPreferences />
The Preferences component lets your users manage which topics they’re subscribed to and how each topic is delivered (per-channel routing and digest schedules), directly inside your Vue app.
Authentication requires a JWT that includes the
read:preferences and write:preferences scopes.CourierPreferences Props
| Prop Name | Type | Default | Description |
|---|---|---|---|
style | StyleValue | undefined | Styles applied to the preferences component. |
lightTheme | CourierPreferencesTheme | undefined | Theme for light mode. Merged with defaults. |
darkTheme | CourierPreferencesTheme | undefined | Theme for dark mode. Merged with defaults. |
mode | "light" | "dark" | "system" | "system" | Theme mode. |
title | string | undefined | Override the component’s title text. |
subtitle | string | undefined | Override the component’s subtitle text. |
brandId | string | undefined | Render preferences using a specific brand’s styling. |
channelLabels | Record<string, string> | undefined | Rename how delivery channels appear in the UI (e.g. { email: "E-mail" }). |
onError | (error: Error) => void | undefined | Callback invoked when the component encounters an error. |
defaultPreferencesLightTheme / defaultPreferencesDarkTheme for defaults, and mergePreferencesTheme(mode, overrideTheme) to merge.
useCourier Composable
TheuseCourier() composable provides programmatic access to Courier functionality for building custom UIs. It returns the same shape as the React hook, but auth, inbox, and toast are Vue shallowRefs — access them with .value in <script setup>, and they auto-unwrap in templates.
When to use the composable vs components
- Components (
<CourierInbox />,<CourierToast />): quick integration with default UI - Composable (
useCourier()): custom UIs, programmatic control, advanced state management - Both together: use the composable for state management while components handle rendering
Composable return value
inbox.totalUnreadCount, inbox.feeds[...]):
Inbox methods
| Method | Description |
|---|---|
inbox.value.load(props?) | Load messages. Optional { canUseCache, datasetIds }. |
inbox.value.fetchNextPageOfMessages({ datasetId }) | Fetch next page. Returns InboxDataSet | null. |
inbox.value.setPaginationLimit(limit) | Set messages per page. |
inbox.value.registerFeeds(feeds) | Register feeds and tabs with the datastore. |
inbox.value.listenForUpdates() | Start WebSocket connection for real-time updates. Required after auth. |
inbox.value.readMessage(message) | Mark as read. |
inbox.value.unreadMessage(message) | Mark as unread. |
inbox.value.archiveMessage(message) | Archive. |
inbox.value.unarchiveMessage(message) | Unarchive. |
inbox.value.clickMessage(message) | Track click event (analytics). |
inbox.value.openMessage(message) | Mark as opened. |
inbox.value.readAllMessages() | Mark all as read. |
Advanced
EU and regional endpoints
Only needed if your workspace uses the EU datacenter.@trycourier/courier-vue re-exports EU_COURIER_API_URLS and getCourierApiUrlsForRegion from @trycourier/courier-js.
Server-side rendering
Courier Inbox and Toast render client-side only. They mount inonMounted (which never runs during SSR), so they work with Nuxt and other SSR setups without extra configuration — the components simply render once on the client.
Troubleshooting
Inbox not updating in real-time
Inbox not updating in real-time
Make sure you’ve called
inbox.value.listenForUpdates() after authentication. This establishes the WebSocket connection required for real-time updates.Messages not loading
Messages not loading
Possible causes:
- Not authenticated — ensure
signIn()has been called - Feeds not registered — call
registerFeeds()beforeload() - Network errors — check
inbox.value.errorfor details - JWT expired — generate a new token
Authentication errors
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:messagesandinbox:write:events - Wrong user ID: Verify the
userIdmatches the user the JWT was issued for
Custom slots not rendering
Custom slots not rendering
Custom render props must return a Vue node built with
h() (imported from vue), not a raw string of markup. Ensure the render prop is bound with :render-list-item="..." (kebab-case) on the component.TypeScript errors
TypeScript errors
Import types directly from the package:
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. Set appropriatesetPaginationLimit()values. The composable’sshallowRefs update automatically; avoid triggering unnecessary re-renders. - Testing: Mock
useCourier()in tests to return test data: