Skip to main content

Adding the Inbox Component

<CourierInbox />

Preview of the default CourierInbox component

Default CourierInbox component

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 with the inbox
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  return <CourierInbox />;
}
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.
Sample App: See a complete working example in our React Inbox sample app.

<CourierInboxPopupMenu />

Preview of the default CourierInboxPopupMenu component

Default CourierInboxPopupMenu component

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 with the inbox
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  return (
    <div style={{ padding: "24px" }}>
      <CourierInboxPopupMenu />
    </div>
  );
}
Sample App: See a complete working example in our React Popup Menu sample app.
Looking for toast notifications? See the Toast Component section below.

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

CourierInbox with many tabs

import { useEffect } from "react";
import {
  CourierInbox,
  useCourier,
  type CourierInboxFeed,
} from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  const feeds: CourierInboxFeed[] = [
    {
      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 }
        }
      ]
    }
  ];

  return <CourierInbox feeds={feeds} />;
}

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

CourierInbox with many feeds

import { useEffect } from "react";
import {
  CourierInbox,
  useCourier,
  type CourierInboxFeed,
} from "@trycourier/courier-react";

export default function App() {

  const courier = useCourier();

  useEffect(() => {
    const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";

    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: jwt,
    });
  }, []);

  const feeds: CourierInboxFeed[] = [
    {
      feedId: 'all',
      title: 'All',
      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'] }
        }
      ]
    }
  ];

  return <CourierInbox feeds={feeds} />;
}

Handle Clicks and Presses

Pass callback functions to the onMessageClick, onMessageActionClick, and onMessageLongPress props to handle clicks and presses in the inbox.
Callback PropType Signature
onMessageClick(props: CourierInboxListItemFactoryProps) => void
onMessageActionClick(props: CourierInboxListItemActionFactoryProps) => void
onMessageLongPress(props: CourierInboxListItemFactoryProps) => void
onMessageLongPress is only applicable on devices that support touch events.
import { useEffect } from "react";
import {
  CourierInbox,
  useCourier,
  type CourierInboxListItemFactoryProps,
  type CourierInboxListItemActionFactoryProps
} 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 with the inbox
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: 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(`Message action clicked at index ${index}:\n` +
              `Action: ${JSON.stringify(action, null, 2)}\n` +
              `Message: ${JSON.stringify(message, null, 2)}`);
      }}

      onMessageLongPress={(
        { message, index }: CourierInboxListItemFactoryProps) => {
        alert("Message long pressed at index " +
              `${index}:\n${JSON.stringify(message, null, 2)}`);
      }}
    />
  );

}

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

Customize both the light and dark themes. The example below shows unread indicator styling applied in both cases.
Courier React Inbox with a custom unread indicator style

Courier React Inbox with a custom unread indicator style

import {
  CourierInbox,
  type CourierInboxTheme
} from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  const theme: CourierInboxTheme = {
    inbox: {
      header: {
        filters: {
          unreadIndicator: {
            backgroundColor: "#8B5CF6",
          },
        },
      },
      list: {
        item: {
          unreadIndicatorColor: "#8B5CF6",
        },
      },
    },
  };

  return <CourierInbox lightTheme={theme} darkTheme={theme} mode="light" />;
}

Theme Utilities

Courier provides utility functions to help you work with themes: Default Themes:
  • defaultLightTheme - Default light theme for inbox
  • defaultDarkTheme - Default dark theme for inbox
  • defaultToastLightTheme - Default light theme for toast
  • defaultToastDarkTheme - Default dark theme for toast
Theme Merging:
  • mergeTheme(baseTheme, overrideTheme) - Merge two inbox themes, with overrideTheme taking precedence
  • mergeToastTheme(baseTheme, overrideTheme) - Merge two toast themes, with overrideTheme taking precedence
import {
  CourierInbox,
  defaultLightTheme,
  mergeTheme,
  type CourierInboxTheme
} from "@trycourier/courier-react";

export default function App() {
  // Start with default theme
  const baseTheme = defaultLightTheme;
  
  // Override specific properties
  const customTheme: CourierInboxTheme = {
    inbox: {
      list: {
        item: {
          unreadIndicatorColor: "#8B5CF6",
        },
      },
    },
  };
  
  // Merge themes
  const mergedTheme = mergeTheme(baseTheme, customTheme);

  return <CourierInbox lightTheme={mergedTheme} mode="light" />;
}
Customizing the popup menu's alignment, position, and dimensions

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"
import { CourierInboxPopupMenu } from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  return (
    <div style={{ display: "flex", justifyContent: "center", alignItems: "center", padding: "100px" }}>
      <CourierInboxPopupMenu
        popupAlignment="top-left"
        popupWidth="340px"
        popupHeight="400px"
        top="44px"
        left="44px"
      />
    </div>
  );

}

Height

<CourierInbox /> 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 prop.
import { CourierInbox } from "@trycourier/courier-react";

export default function App() {
  // Courier authentication and other component code...

  return <CourierInbox height="50vh" />;
}

Custom Elements

Element Refs

You can use React refs to get programmatic access to component instances. This allows you to call methods on the components, such as removeHeader() on the inbox component. The following element types are available for refs:
  • CourierInboxElement - Ref type for <CourierInbox />
  • CourierInboxPopupMenuElement - Ref type for <CourierInboxPopupMenu />
  • CourierToastElement - Ref type for <CourierToast />
import { useEffect, useRef } from 'react';
import {
  CourierInbox,
  useCourier,
  type CourierInboxElement,
} from '@trycourier/courier-react';

export default function App() {
  const courier = useCourier();
  const inboxRef = useRef<CourierInboxElement>(null);

  useEffect(() => {
    courier.shared.signIn({
      userId: $YOUR_USER_ID,
      jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      showLogs: false,
    });
  }, []);

  useEffect(() => {
    if (inboxRef.current) {
      // Remove the header programmatically
      inboxRef.current.removeHeader();
    }
  }, [inboxRef.current]);

  return <CourierInbox ref={inboxRef} />;
}
Available Ref Methods:
  • removeHeader() - Removes the header from the <CourierInbox /> component
Ref methods are component-specific. Check the component’s type definition for all available methods.

List Items

Render PropType Signature
renderListItem(props: CourierInboxListItemFactoryProps) => ReactNode
Customize the inbox message list item by passing a React component to the renderListItem prop.
Custom inbox message list item displaying the message object

Custom inbox message list item displaying the message object

import {
  CourierInbox,
  type CourierInboxListItemFactoryProps
} from "@trycourier/courier-react"

const CustomListItem = (
  { message, index }: CourierInboxListItemFactoryProps) => (
    <pre style={{
      padding: "24px",
      borderBottom: "1px solid #e0e0e0",
      margin: "0"
    }}>
      {JSON.stringify({ message, index }, null, 2)}
    </pre>
  );

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierInbox
      renderListItem={(props: CourierInboxListItemFactoryProps) => {
        return <CustomListItem {...props} />
      }}
    />
  );
}

Custom Header

Render PropType Signature
renderHeader(props: CourierInboxHeaderFactoryProps) => ReactNode
Customize the inbox header by passing a React component to the renderHeader prop.
Custom inbox header

Custom inbox header

import {
  CourierInbox,
  type CourierInboxHeaderFactoryProps
} from "@trycourier/courier-react";

const CustomHeader = (props: CourierInboxHeaderFactoryProps) => {
  const selectedFeed = props.feeds.find(feed => feed.isSelected);
  const selectedTab = selectedFeed?.tabs.find(tab => tab.isSelected);

  return (
    <div style={{
      background: "red",
      fontSize: "24px",
      padding: "24px",
      width: "100%"
    }}>
      Selected Feed: {selectedFeed?.title ?? 'None'} | 
      Selected Tab: {selectedTab?.title ?? 'None'} | 
      Unread: {selectedTab?.unreadCount ?? 0}
    </div>
  );
};

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierInbox
      renderHeader={(props: CourierInboxHeaderFactoryProps) => {
        return <CustomHeader {...props} />
      }}
    />
  );
}
Render PropType Signature
renderMenuButton(props: CourierInboxMenuButtonFactoryProps) => ReactNode
Customize the inbox popup menu button by passing a React component to the renderMenuButton prop.
Custom inbox popup menu button

Custom inbox popup menu button

import {
  CourierInboxPopupMenu,
  type CourierInboxMenuButtonFactoryProps
} from "@trycourier/courier-react"

const CustomMenuButton = ({ unreadCount }: CourierInboxMenuButtonFactoryProps) => (
  <button>
    Open the Inbox Popup. Unread message count: {unreadCount}
  </button>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <div style={{ padding: "24px" }}>
      <CourierInboxPopupMenu
        renderMenuButton={(props: CourierInboxMenuButtonFactoryProps) => {
          return <CustomMenuButton {...props} />
        }}
      />
    </div>
  );
}

Loading, Empty, Error States and Custom Pagination

Render PropType Signature
renderLoadingState(props: CourierInboxStateLoadingFactoryProps) => ReactNode
renderEmptyState(props: CourierInboxStateEmptyFactoryProps) => ReactNode
renderErrorState(props: CourierInboxStateErrorFactoryProps) => ReactNode
renderPaginationItem(props: CourierInboxPaginationItemFactoryProps) => ReactNode
Customize the inbox loading, empty, error states and pagination item by passing a React component to the renderLoadingState, renderEmptyState, renderErrorState, and renderPaginationItem props.
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

Custom pagination state

import {
  CourierInbox,
  type CourierInboxStateEmptyFactoryProps,
  type CourierInboxStateLoadingFactoryProps,
  type CourierInboxStateErrorFactoryProps,
  type CourierInboxPaginationItemFactoryProps
} from "@trycourier/courier-react"

const CustomLoadingState = ({ datasetId }: CourierInboxStateLoadingFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "red",
    textAlign: "center"
  }}>
    Custom Loading State for {datasetId}
  </div>
);

const CustomEmptyState = ({ datasetId }: CourierInboxStateEmptyFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "green",
    textAlign: "center"
  }}>
    Custom Empty State for {datasetId}
  </div>
);

const CustomErrorState = ({ datasetId, error }: CourierInboxStateErrorFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "blue",
    textAlign: "center"
  }}>
    Custom Error State for {datasetId}: {error.message}
  </div>
);

const CustomPaginationItem = ({ datasetId }: CourierInboxPaginationItemFactoryProps) => (
  <div style={{
    padding: "24px",
    background: "#f0f0f0",
    textAlign: "center"
  }}>
    🔄 Loading the next page of <b>{datasetId}</b> messages
  </div>
);

export default function App() {
  // Courier authentication and other component code...

  return (
    <CourierInbox
      renderLoadingState={(props: CourierInboxStateLoadingFactoryProps) => {
        return <CustomLoadingState {...props} />
      }}
      renderEmptyState={(props: CourierInboxStateEmptyFactoryProps) => {
        return <CustomEmptyState {...props} />
      }}
      renderErrorState={(props: CourierInboxStateErrorFactoryProps) => {
        return <CustomErrorState {...props} />
      }}
      renderPaginationItem={(props: CourierInboxPaginationItemFactoryProps) => {
        return <CustomPaginationItem {...props} />
      }}
    />
  );

}