UNPKG

@trycourier/courier-react

Version:

The React components for the Courier web UI

412 lines (309 loc) 11.1 kB
<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)