Skip to main content
Inbox UI Preview
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.

Installation

Available on GitHub and npm.
npm install @trycourier/courier-ui-inbox
The Courier SDKs work with any JavaScript build system and do not require any additional build configuration.
Using React? Check out the Courier Inbox React Components.

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"
  }'

Inbox Web Components

<courier-inbox>

Default Courier Inbox component
Importing the Courier SDK registers Courier’s Web Components (<courier-inbox>, <courier-inbox-popup-menu>).
<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>
Sample App: See a complete working example in our Vanilla JS Inbox sample app.
If you’re using tenants, you can scope requests to a particular tenant by passing its ID to the signIn request.
Courier.shared.signIn({
  userId: "my-user-id",
  jwt: jwt,
  tenantId: "my-tenant-id"
});
For the full reference of sign in parameters, see the Courier JS docs.

<courier-inbox-popup-menu>

Default Courier Inbox Popup Menu component
Importing the Courier SDK registers Courier’s Web Components (<courier-inbox>, <courier-inbox-popup-menu>).
<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>
Sample App: See a complete working example in our Vanilla JS Popup Menu sample app.

Tabs and Feeds

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.

Understanding Tabs and Feeds

  • 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.

Tabs

You can define multiple tabs inside of a feed. Each tab appears in the list and will apply it’s filters to the messages.
Preview of CourierInbox with tabs
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');

    // Set feeds with multiple tabs
    inbox.setFeeds([
      {
        feedId: 'notifications',
        title: 'Notifications',
        tabs: [
          {
            datasetId: 'all-notifications',
            title: 'All',
            filter: {}
          },
          {
            datasetId: 'unread-notifications',
            title: 'Unread',
            filter: { status: 'unread' }
          },
          {
            datasetId: 'read-notifications',
            title: 'Read',
            filter: { status: 'read' }
          },
          {
            datasetId: 'important',
            title: 'Important',
            filter: { tags: ['important'] }
          },
          {
            datasetId: 'archived',
            title: 'Archived',
            filter: { archived: true }
          }
        ]
      }
    ]);

    // Authenticate the user
    Courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
    });
  </script>
</body>

Filter Options

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 PropertyTypeDescription
tagsstring[]Messages that have any of the specified tags
archivedbooleanWhether to include archived messages (defaults to false if unset)
status'read' | 'unread'Filter by read/unread status

Feeds

You can define multiple feeds to organize messages into different categories. Each feed appears as a selectable option in the inbox header.
Preview of CourierInbox with feeds
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');

    // Set multiple feeds
    inbox.setFeeds([
      {
        feedId: 'all',
        title: 'All',
        iconSVG: '...',
        tabs: [
          {
            datasetId: 'all',
            title: 'All',
            filter: {}
          }
        ]
      },
      {
        feedId: 'jobs',
        title: 'Jobs',
        tabs: [
          {
            datasetId: 'jobs',
            title: 'Jobs',
            filter: { tags: ['job'] }
          }
        ]
      },
      {
        feedId: 'my-posts',
        title: 'My Posts',
        tabs: [
          {
            datasetId: 'all-my-posts',
            title: 'All',
            filter: {}
          },
          {
            datasetId: 'comments',
            title: 'Comments',
            filter: { tags: ['comment'] }
          },
          {
            datasetId: 'reactions',
            title: 'Reactions',
            filter: { tags: ['reaction'] }
          },
          {
            datasetId: 'reposts',
            title: 'Reposts',
            filter: { tags: ['repost'] }
          }
        ]
      },
      {
        feedId: 'mentions',
        title: 'Mentions',
        tabs: [
          {
            datasetId: 'mentions',
            title: 'Mentions',
            filter: { tags: ['mention'] }
          }
        ]
      }
    ]);

    // Authenticate the user
    Courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
    });
  </script>
</body>

Handle Clicks and Presses

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.
<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>

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

Courier Inbox with a custom unread indicator style
<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';

    // Courier authentication...

    const inbox = document.getElementById('inbox');
    const theme = {
      inbox: {
        header: {
          filters: {
            unreadIndicator: {
              backgroundColor: "#8B5CF6"
            }
          }
        },
        list: {
          item: {
            unreadIndicatorColor: "#8B5CF6"
          }
        }
      }
    };

    // Apply the theme
    inbox.setLightTheme(theme);
    inbox.setDarkTheme(theme);

    // Set the mode ('light', 'dark', or 'system')
    inbox.setMode('light');
  </script>
</body>
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"
<body>
  <div style="display: flex; justify-content: center; align-items: center; padding: 100px;">

    <courier-inbox-popup-menu
      popup-alignment="top-right"
      top="44px"
      right="44px"
      popup-width="340px"
      popup-height="400px">
    </courier-inbox-popup-menu>
  </div>
</body>

Height

<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.
<body>
  <courier-inbox height="50vh"></courier-inbox>
</body>

Custom Elements

List Items

Customize the inbox message list item by passing an HTML element to the setListItem method on the <courier-inbox> component.
Custom inbox message list item displaying the message object
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    // Reference the courier-inbox element
    const inbox = document.getElementById('inbox');

    // Set a custom list item
    inbox.setListItem(({ message, index }) => {
      const pre = document.createElement('pre');
      pre.style.padding = '24px';
      pre.style.borderBottom = '1px solid #e0e0e0';
      pre.style.margin = '0';
      pre.textContent = JSON.stringify({ message, index }, null, 2);

      return pre;
    });
  </script>
</body>

Custom Header

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.
Custom inbox header
<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>
Customize the inbox popup menu button by passing an HTML element to the setMenuButton method on the <courier-inbox-popup-menu> component.
Custom inbox popup menu button
<body>
  <div style="display: flex; justify-content: center; align-items: center; padding: 100px;">
    <courier-inbox-popup-menu id="inbox"></courier-inbox-popup-menu>
  </div>

  <script type="module">
    // Reference the courier-inbox element
    const inbox = document.getElementById('inbox');

    // Set a custom menu button
    inbox.setMenuButton(({ unreadCount }) => {
      const button = document.createElement('button');
      button.textContent = `Open the Inbox Popup. Unread message count: ${unreadCount}`;
      return button;
    });
  </script>
</body>

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.
Custom pagination state
<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>

Programmatic Control

The <courier-inbox> component exposes methods for programmatic control, allowing you to dynamically manage feeds, tabs, actions, and data refresh.
For React-specific implementations, see the React SDK hooks documentation.

Feed and Tab Selection

Programmatically switch between feeds and tabs:
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');

    // Set up feeds
    inbox.setFeeds([
      {
        feedId: 'notifications',
        title: 'Notifications',
        tabs: [
          { datasetId: 'all', title: 'All', filter: {} },
          { datasetId: 'unread', title: 'Unread', filter: { status: 'unread' } }
        ]
      },
      {
        feedId: 'archive',
        title: 'Archive',
        tabs: [
          { datasetId: 'archived', title: 'Archived', filter: { archived: true } }
        ]
      }
    ]);

    // Authenticate
    Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: '...' });

    // Programmatically select a feed
    inbox.selectFeed('notifications');

    // Programmatically select a tab within the current feed
    inbox.selectTab('unread');

    // Get current feed ID
    console.log('Current feed:', inbox.currentFeedId);

    // Get all configured feeds
    const feeds = inbox.getFeeds();
    console.log('All feeds:', feeds);
  </script>
</body>

selectFeed(feedId: string)

Switches to the specified feed and loads its data. If the feed is already selected, resets to the first tab.
<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 a different feed
  inbox.selectFeed('archive');
</script>
Parameters:
  • feedId (string) - The ID of the feed to select
Throws: Error if feed doesn’t exist or has no tabs

selectTab(tabId: string)

Switches to the specified tab within the current feed and loads its data.
<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

getFeeds()

Returns the current array of feeds configured for the inbox.
<script type="module">
  const inbox = document.getElementById('inbox');

  // Get all configured feeds
  const feeds = inbox.getFeeds();
  console.log('Available feeds:', feeds);
  // [
  //   {
  //     feedId: 'notifications',
  //     title: 'Notifications',
  //     tabs: [...]
  //   },
  //   ...
  // ]
</script>
Returns: CourierInboxFeed[] - Array of feed objects

currentFeedId (getter)

Returns the ID of the currently selected feed.
<script type="module">
  const inbox = document.getElementById('inbox');

  // Get the current feed ID
  const currentFeed = inbox.currentFeedId;
  console.log('Current feed:', currentFeed);
  // 'notifications'
</script>
Returns: string - Current feed ID

Data Refresh

refresh()

Forces a reload of inbox data, bypassing the cache. Useful when you need to fetch the latest messages from the server.
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');
    Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: '...' });

    // Refresh inbox data (bypasses cache)
    inbox.refresh().then(() => {
      console.log('Inbox refreshed');
    });
  </script>
</body>
Returns: Promise<void> Note: Requires user to be signed in. Will log an error if no user is authenticated.

Actions Configuration

Configure which actions are available in the inbox header and on list items.

setActions(actions: CourierInboxHeaderAction[])

Sets the header actions (e.g., “Mark All Read”, “Archive Read”, “Archive All”). Pass an empty array to remove all actions.
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');

    // Configure header actions
    inbox.setActions([
      { id: 'readAll', iconSVG: '...', text: 'Mark All Read' },
      { id: 'archiveRead', iconSVG: '...', text: 'Archive Read' }
    ]);

    // Remove all header actions
    // inbox.setActions([]);
  </script>
</body>
Parameters:
  • actions (CourierInboxHeaderAction[]) - Array of action configurations
Action IDs:
  • 'readAll' - Mark all messages as read
  • 'archiveRead' - Archive all read messages
  • 'archiveAll' - Archive all messages

setListItemActions(actions: CourierInboxListItemAction[])

Sets the actions available on individual list items (e.g., “Mark Read/Unread”, “Archive/Unarchive”). Pass an empty array to remove all actions.
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');

    // Configure list item actions
    inbox.setListItemActions([
      { 
        id: 'read_unread',
        readIconSVG: '...',      // Icon when message is read
        unreadIconSVG: '...'     // Icon when message is unread
      },
      {
        id: 'archive_unarchive',
        archiveIconSVG: '...',   // Icon when message is not archived
        unarchiveIconSVG: '...'  // Icon when message is archived
      }
    ]);

    // Remove all list item actions
    // inbox.setListItemActions([]);
  </script>
</body>
Parameters:
  • actions (CourierInboxListItemAction[]) - Array of action configurations
Action IDs:
  • 'read_unread' - Toggle read/unread status
  • 'archive_unarchive' - Toggle archive status

Helper Methods

CourierInbox.defaultFeeds()

Returns the default set of feeds (Inbox and Archive) used by CourierInbox. Useful for building custom UIs that match the default structure.
<script type="module">
  import { CourierInbox } from '@trycourier/courier-ui-inbox';

  // Get default feeds
  const defaultFeeds = CourierInbox.defaultFeeds();
  console.log(defaultFeeds);
  // Returns:
  // [
  //   {
  //     feedId: 'inbox_feed',
  //     title: 'Inbox',
  //     tabs: [{ datasetId: 'all_messages', title: 'All Messages', filter: {} }]
  //   },
  //   {
  //     feedId: 'archive_feed',
  //     title: 'Archive',
  //     tabs: [{ datasetId: 'archived_messages', title: 'Archived Messages', filter: { archived: true } }]
  //   }
  // ]
</script>
Returns: CourierInboxFeed[] - Default feeds array

CourierInbox.defaultActions()

Returns the default header actions configuration.
<script type="module">
  import { CourierInbox } from '@trycourier/courier-ui-inbox';

  const defaultActions = CourierInbox.defaultActions();
  // Returns: [{ id: 'readAll' }, { id: 'archiveRead' }, { id: 'archiveAll' }]
</script>
Returns: CourierInboxHeaderAction[] - Default header actions

CourierInbox.defaultListItemActions()

Returns the default list item actions configuration.
<script type="module">
  import { CourierInbox } from '@trycourier/courier-ui-inbox';

  const defaultActions = CourierInbox.defaultListItemActions();
  // Returns: [{ id: 'read_unread' }, { id: 'archive_unarchive' }]
</script>
Returns: CourierInboxListItemAction[] - Default list item actions

Getting Current State

getHeaderFeeds()

Returns the header feeds in the format expected by header factory functions. Includes selection state, unread counts, and filter information.
<body>
  <courier-inbox id="inbox"></courier-inbox>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';

    const inbox = document.getElementById('inbox');
    Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: '...' });

    // Get header feeds (useful for custom headers)
    const headerFeeds = inbox.getHeaderFeeds();
    console.log(headerFeeds);
    // Returns array with structure:
    // [
    //   {
    //     feedId: string,
    //     title: string,
    //     iconSVG?: string,
    //     tabs: [
    //       {
    //         datasetId: string,
    //         title: string,
    //         unreadCount: number,
    //         isSelected: boolean,
    //         filter: { ... }
    //       }
    //     ],
    //     isSelected: boolean
    //   }
    // ]
  </script>
</body>
Returns: CourierInboxHeaderFeed[] - Header feeds in factory format

theme (getter)

Returns the current theme object being applied to the inbox.
<script type="module">
  const inbox = document.getElementById('inbox');
  const currentTheme = inbox.theme;
  console.log('Current theme:', currentTheme);
</script>
Returns: CourierInboxTheme - Current theme object The <courier-inbox-popup-menu> component extends <courier-inbox> and adds methods for controlling the popup visibility.

showPopup()

Opens the popup menu programmatically with a transition animation.
<body>
  <courier-inbox-popup-menu id="inbox"></courier-inbox-popup-menu>
  <button onclick="document.getElementById('inbox').showPopup()">
    Open Inbox
  </button>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';
    Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: '...' });
  </script>
</body>
Note: The popup will automatically open when the menu button is clicked, but you can also trigger it programmatically.

closePopup()

Closes the popup menu programmatically with a transition animation.
<body>
  <courier-inbox-popup-menu id="inbox"></courier-inbox-popup-menu>
  <button onclick="document.getElementById('inbox').closePopup()">
    Close Inbox
  </button>

  <script type="module">
    import { Courier } from '@trycourier/courier-ui-inbox';
    Courier.shared.signIn({ userId: $YOUR_USER_ID, jwt: '...' });
  </script>
</body>
Note: The popup will automatically close when clicking outside of it, but you can also close it programmatically.

Attribute vs Method Usage

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
<!-- 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.