@trycourier/courier-react
Version:
The React components for the Courier web UI
412 lines (309 loc) • 11.1 kB
Markdown
<img width="1040" alt="courier-react" src="https://github.com/user-attachments/assets/e886b445-d106-4dab-afca-82183e0fcbe7" />
## 1. Install
```sh
npm install @trycourier/courier-react
```
> **Not using React?** We suggest you use [@trycourier/courier-ui-inbox](../courier-ui-inbox/README.md) package instead.
## 2. Authenticate
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.**
**How it works:**
1. **Your frontend calls your backend:**
- When your app needs to authenticate a user, your frontend should make a request to your own backend (e.g., `/api/generate-courier-jwt`).
2. **Your backend calls Courier to issue a JWT:**
- In your backend endpoint, use your [Courier API Key](https://app.courier.com/settings/api-keys) to call the [Courier JWT Token Endpoint](https://www.courier.com/docs/reference/auth/issue-token) and generate a JWT for the user.
- Your backend then returns the JWT to your frontend.
To quickly test JWT generation (for development only), you can use the following cURL command to call Courier's API directly. **Do not use this in production or from client-side code.**
```sh
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"
}'
```
## 3. Add Inbox Component
### `CourierInbox`
<img width="688" alt="Screenshot 2025-06-25 at 5 17 47 PM" src="https://github.com/user-attachments/assets/1655f43b-cc61-473f-9204-8dffeae21042" />
```ts
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 (do this on your backend server)
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // Replace with actual JWT
// Authenticate the user with the inbox
courier.shared.signIn({
userId: $YOUR_USER_ID,
jwt: jwt,
});
}, []);
return <CourierInbox />;
}
```
### `CourierInboxPopupMenu`
<img width="605" alt="Screenshot 2025-06-25 at 5 21 53 PM" src="https://github.com/user-attachments/assets/1c5497ba-a860-4d7e-8cf7-5b56a65cea51" />
```ts
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 (do this on your backend server)
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // Replace with actual JWT
// Authenticate the user with the inbox
courier.shared.signIn({
userId: $YOUR_USER_ID,
jwt: jwt,
});
}, []);
return (
<div style={{ padding: '24px' }}>
<CourierInboxPopupMenu />
</div>
);
}
```
## NextJS & Server Side Rendering Frameworks
`courier-react` only supports client side rendering. You will need to add `'use client'` to the top of any file that will use `CourierInbox` or `CourierInboxPopupMenu`.
```ts
'use client'
...
import { ..., CourierInboxPopupMenu } from '@trycourier/courier-react';
export default function Page() {
...
return <CourierInboxPopupMenu />;
}
```
## Handle Clicks and Presses
```ts
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 (do this on your backend server)
const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // Replace with actual JWT
// 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) => {
// Handle message long presses. **Only works on devices that support javascript's touch events. This will not work with a mouse cursor.**
alert("Message long pressed at index " + index + ":\n" + JSON.stringify(message, null, 2));
}}
/>
);
}
```
## Styles and Theming
### Light & Dark Themes
The fastest way to style the Inbox to match your app. This example shows unread indicator styling, but you can customize fonts, icons, text, and more.
> **🎨 Theme Reference:** [All available theme values](../courier-ui-inbox/docs/theme.md)
<img width="688" alt="Screenshot 2025-06-25 at 5 38 07 PM" src="https://github.com/user-attachments/assets/d1440494-ee66-4ff6-850b-c1a13691cf2f" />
```ts
import { CourierInbox, ..., type CourierInboxTheme } from '@trycourier/courier-react';
export default function App() {
...
const theme: CourierInboxTheme = {
inbox: {
header: {
filters: {
unreadIndicator: {
backgroundColor: '#8B5CF6',
},
},
},
list: {
item: {
unreadIndicatorColor: '#8B5CF6',
},
},
},
};
return <CourierInbox lightTheme={theme} darkTheme={theme} mode="light" />;
}
```
### Popup Alignment, Positioning, and Dimensions
```ts
export default function App() {
...
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', padding: '100px' }}>
<CourierInboxPopupMenu
popupAlignment="top-right" // 'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center' | 'center-right' | 'center-left' | 'center-center'
popupWidth="340px"
popupHeight="400px"
top="44px"
right="44px"
/>
</div>
);
}
```
### Custom height `CourierInbox`
> **Important:** The default `CourierInbox` height is auto. It will set it's height based on it's children.
```ts
<CourierInbox height='50vh' />
```
## Custom Elements
Customize the inbox UI with any element you want.
### List Items
<img width="688" alt="Screenshot 2025-06-25 at 6 25 17 PM" src="https://github.com/user-attachments/assets/075a568b-5538-4116-bb3d-752ecc5dea2c" />
```ts
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() {
...
return (
<CourierInbox
renderListItem={(props: CourierInboxListItemFactoryProps) => {
return <CustomListItem {...props} />
}}
/>
);
}
```
### Header
<img width="688" alt="Screenshot 2025-06-25 at 6 35 59 PM" src="https://github.com/user-attachments/assets/25d45ac1-32e6-4e98-baf1-d0df34cef72a" />
```ts
import { CourierInbox, ..., type CourierInboxHeaderFactoryProps } from '@trycourier/courier-react'
const CustomHeader = (props: CourierInboxHeaderFactoryProps) => (
<div style={{
background: 'red',
fontSize: '24px',
padding: '24px',
width: '100%'
}}>
{props.feedType}
</div>
);
export default function App() {
...
return (
<CourierInbox
renderHeader={(props: CourierInboxHeaderFactoryProps) => {
return <CustomHeader {...props} />
}}
/>
);
}
```
### Popup Menu Button
<img width="606" alt="Screenshot 2025-06-25 at 6 40 38 PM" src="https://github.com/user-attachments/assets/dfb0daca-f11d-420f-9dc7-0c14b49ab7db" />
```ts
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() {
...
return (
<div style={{ padding: '24px' }}>
<CourierInboxPopupMenu
renderMenuButton={(props: CourierInboxMenuButtonFactoryProps) => {
return <CustomMenuButton {...props} />
}}
/>
</div>
);
}
```
### Loading, Empty, Error & Pagination
```ts
import {
CourierInbox,
...,
type CourierInboxStateEmptyFactoryProps,
type CourierInboxStateLoadingFactoryProps,
type CourierInboxStateErrorFactoryProps,
type CourierInboxPaginationItemFactoryProps
} from '@trycourier/courier-react'
const CustomLoadingState = ({ feedType }: CourierInboxStateLoadingFactoryProps) => (
<div style={{
padding: '24px',
background: 'red',
textAlign: 'center'
}}>
Custom Loading State
</div>
);
const CustomEmptyState = ({ feedType }: CourierInboxStateEmptyFactoryProps) => (
<div style={{
padding: '24px',
background: 'green',
textAlign: 'center'
}}>
Custom Empty State
</div>
);
const CustomErrorState = ({ feedType, error }: CourierInboxStateErrorFactoryProps) => (
<div style={{
padding: '24px',
background: 'blue',
textAlign: 'center'
}}>
Custom Error State: {error.message}
</div>
);
const CustomPaginationItem = ({ feedType }: CourierInboxPaginationItemFactoryProps) => (
<div style={{
padding: '24px',
background: 'yellow',
textAlign: 'center'
}}>
Custom Pagination Item
</div>
);
export default function App() {
...
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} />
}}
/>
);
}
```
> **Not using React?** We suggest you use [@trycourier/courier-ui-inbox](../courier-ui-inbox/README.md) package instead.
# **Share feedback with Courier**
We want to make this the best SDK for managing notifications! Have an idea or feedback about our SDKs? Let us know!
[Courier Web Issues](https://github.com/trycourier/courier-web/issues)