> ## Documentation Index
> Fetch the complete documentation index at: https://docs.suprsend.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build your own feed UI (headless)

> A comprehensive guide to integrating a feed using React hooks to design and build a custom UI for your feed in a React app

To implement your own UI for Feed in your react application we provide context providers and hooks.

<Note>
  If you are not using any of the [In-build UI
  components](https://github.com/suprsend/suprsend-react-sdk?tab=readme-ov-file#suprsend-react-sdk) while
  designing your custom UI for feed, you can
  use [@suprsend/react-core](https://github.com/suprsend/suprsend-react-core) SDK.
  These hooks and context providers are available in
  both `@suprsend/react-core` and `@suprsend/react` SDK's. Integration steps
  mentioned in this guide are also same for both.
</Note>

### Pre-Requisite

Integrate [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider) as it is needed for creating SuprSend Client and authenticating user.

## Installation

<CodeGroup>
  ```javascript npm theme={"system"}
  npm install @suprsend/react-core
  ```

  ```
  yarn add @suprsend/react-core
  ```
</CodeGroup>

<Warning>
  In `@suprsend/react-core` v1.0.0, we have changed the `pageInfo` object data.
  Please refer to [Migration
  guide](https://docs.suprsend.com/docs/migration-guide-from-v1#migrating-to-v4-from-v3)
  for more details.
</Warning>

## Integration

### SuprSendFeedProvider

This provider internally creates InApp feed client instance and also removes it on unmounting. This should be called inside [SuprSendProvider](https://docs.suprsend.com/docs/react-1#suprsendprovider). FeedClient can be accessed using [useFeedClient](https://docs.suprsend.com/docs/headless-feed#usefeedclient) hook.

<CodeGroup>
  ```javascript Example.jsx theme={"system"}
  import { SuprSendProvider, SuprSendFeedProvider } from '@suprsend/react-core';
  // import { SuprSendProvider, SuprSendFeedProvider } from '@suprsend/react'; // if you use our inbuilt react components

  function Example() {
    return (
      <SuprSendProvider publicApiKey={YOUR_KEY} distinctId={YOUR_DISTINCT_ID}>
        <SuprSendFeedProvider>Your Feed Component</SuprSendFeedProvider>
      </SuprSendProvider>
    );
  }

  ```

  ```javascript TypeDef theme={"system"}
  interface SuprSendFeedProviderProps {
    tenantId?: string;
    pageSize?: number;
    stores?: IStore[] | null;
    host?: { socketHost?: string, apiHost?: string };
  }
  ```
</CodeGroup>

### useFeedClient

This hook is used to get Feed client instance. Using this instance you can access [feed methods](https://docs.suprsend.com/docs/inapp-feed#other-methods) like mark seen, archive, read etc. Use this hook inside [SuprSendFeedProvider](https://docs.suprsend.com/docs/headless-feed#suprsendfeedprovider).

<CodeGroup>
  ```javascript Example.jsx theme={"system"}
  import {useFeedClient} from '@suprsend/react-core';
  // import { useFeedClient } from '@suprsend/react'; // if you use our inbuilt react components

  function MyComponent() {
  const feedClient = useFeedClient();

  return (
    <p
      onClick={() => {
        feedClient.markAsSeen(notificationId);
      }}
    >
      Click Me
    </p>
  );
  }

  ```
</CodeGroup>

### useFeedData

This hook returns react state that contains notification store data which includes notifications list and other meta data. This state gets updated internally when there is any update in store. Use this state to render UI in your application. Use this hook inside [SuprSendFeedProvider](https://docs.suprsend.com/docs/headless-feed#suprsendfeedprovider).

<CodeGroup>
  ```javascript Example.jsx theme={"system"}
  import {useFeedData} from "@suprsend/react-core"
  // import { useFeedData } from '@suprsend/react'; // if you use our inbuilt react components


  function MyComponent() {
    const feedData = useFeedData();

    const notificationsList = feedData.notifications;
    return <div>{notificationsList.map(notification)=><p>{notification.n_id}</p>}</div>;
  }
  ```
</CodeGroup>

### Example

<CodeGroup>
  ```javascript Example.jsx theme={"system"}
  import {
    SuprSendProvider,
    SuprSendFeedProvider,
    useFeedData,
    useFeedClient,
  } from "@suprsend/react-core";

  function Example() {
    return (
      <SuprSendProvider
        publicApiKey={"YOUR_PUBLIC_API_KEY"}
        distinctId={"YOUR_DISTINCT_ID"}
      >
        <SuprSendFeedProvider>
          <MyFeedComponent />
        </SuprSendFeedProvider>
      </SuprSendProvider>
    );
  }

  function MyFeedComponent() {
    const feedData = useFeedData();
    const feedInstance = useFeedClient();

    if (!feedData) return null;
    if (feedData.apiStatus === "LOADING") return <p>Loading Data</p>;
    if (feedData.apiStatus === "SUCCESS" && !feedData?.notifications?.length) {
      return <p>No Notifications</p>;
    }
    if (feedData.notifications) {
      return (
        <div>
          <div>
            {feedData.notifications.map((notification) => {
              return (
                <div
                  key={notification.n_id}
                  onClick={() => {
                    feedInstance.markAsRead(notification.n_id);
                  }}
                >
                  {notification.n_id}
                </div>
              );
            })}
          </div>
          {feedData.apiStatus === "FETCHING_MORE" ? (
            <p>Loading More</p>
          ) : (
            <div>
              {feedData.pageInfo.hasMore && (
                <button
                  onClick={() => {
                    feedInstance.fetchNextPage();
                  }}
                >
                  Next
                </button>
              )}
            </div>
          )}
        </div>
      );
    }
    return null;
  }

  ```
</CodeGroup>

## Using built-in components in headless implementation

We exported few inbuilt components you can use to save time while building your own UI. If you are using `@suprsend/react-core` and want to use these components please remove that package and install `@suprsend/react`, integration steps remain unchanged other than changing import statement.

### NotificationCard component

This is single notification card component. It will be handy if you want to implement your own UI but want to just use our notification card as it has a bit complex logic and UI.

<CodeGroup>
  ```javascript Example.jsx theme={"system"}
  import { NotificationCard } from '@suprsend/react';

  <NotificationCard notificationData={data} />
  ```

  ```
  interface NotificationCardProps {
    notificationData: IRemoteNotification;
    notificationClickHandler?: (notificationData: IRemoteNotification) => void;
    notificationComponent?: React.FC<CustomNotificationCard>;
    hideAvatar?: boolean;
    themeType?: ThemeType;
    primaryActionClickHandler?: (notification: IRemoteNotification) => void;
    secondaryActionClickHandler?: (notification: IRemoteNotification) => void;
    theme?: INotificationCardTheme;
  }
  ```
</CodeGroup>

> Please refer [Customising CSS styles](https://docs.suprsend.com/docs/customising-feed#customising-css-styles) section to view typedefs for `INotificationCardTheme`.

### BodyMarkdown component

This is simple div element but also supports rendering of markdown language. [Read more](https://docs.suprsend.com/docs/customising-feed#bodymarkdown-component) about the component.

#### Sample Notification JSON Structure

```json theme={"system"}
{
  "tenant_id": "default",
  "is_expiry_visible": false,
  "seen_on": 1754346745613,
  "read_on": 1754346745613,
  "is_pinned": false,
  "archived": false,
  "created_on": 1753966489304,
  "n_category": "transactional",
  "interacted_on": 1754346745613,
  "n_id": "01K1G8SBGS4A1QG2CGYSVXQ517",
  "message": {
    "schema": "1.0",
    "header": "Your invoice for {{event.billing_month}} is ready",
    "text": "An invoice of **{{event.amount}}** has been generated for your workspace {{event.workspace_name}}.",
    "subtext": {
      "text": "View full billing history",
      "action_url": "https://app.suprsend.com/billing/history"
    },
    "avatar": {
      "avatar_url": "https://cdn-icons-png.flaticon.com/512/3144/3144456.png",
      "action_url": "https://app.suprsend.com/settings"
    },
    "url": "https://app.suprsend.com/billing/{{event.invoice_id}}",
    "extra_data": "{\n  \"workspace\": \"{{event.workspace_name}}\"\n}",
    "tags": [
      "billing",
      "finance"
    ],
    "expiry": {
      "format": "absolute",
      "expiry_type": "fixed",
      "is_expiry_visible": true,
      "value": "2025-09-30T23:59:59Z"
    },
    "is_pinned": true,
    "is_expiry_enabled": true,
    "actions": [
      {
        "url": "https://app.suprsend.com/billing/{{event.invoice_id}}",
        "name": "View Invoice"
      },
      {
        "url": "https://app.suprsend.com/billing/pay/{{event.invoice_id}}",
        "name": "Pay Now"
      }
    ]
  }
}
```

**Fields and its description**

**Top-level Fields**

| Field               | Type    | Description                                                                                                                                                                                                                 |
| ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `n_id`              | string  | Unique notification ID for a message.                                                                                                                                                                                       |
| `n_category`        | string  | [Category of the notification](/docs/notification-category). Used to fetch and apply category-level preferences (e.g., `transactional`, `marketing`). You can also use categories to group notifications in different tabs. |
| `created_on`        | number  | Timestamp (epoch ms) when the notification reached the inbox.                                                                                                                                                               |
| `seen_on`           | number  | Timestamp (epoch ms) when the notification was first seen.                                                                                                                                                                  |
| `read_on`           | number  | Timestamp (epoch ms) when the notification was marked as read using the `markAsRead` method.                                                                                                                                |
| `interacted_on`     | number  | Timestamp (epoch ms) when the user clicked or interacted with the notification.                                                                                                                                             |
| `message`           | object  | Payload containing the actual notification content and metadata.                                                                                                                                                            |
| `tenant_id`         | string  | [Tenant identifier](/docs/tenants). Used to identify the tenant in a multi-tenant setup.                                                                                                                                    |
| `is_pinned`         | boolean | Whether the notification is pinned in the inbox. Pinned notifications remain fixed at the top of the inbox feed. Commonly used for critical messages that should not scroll down as new notifications arrive.               |
| `archived`          | boolean | Whether the notification has been archived by the user. Archived notifications are hidden from the inbox feed by default unless you explicitly pass `is_archived = false` in the [store config](docs/multi-tabs).           |
| `is_expiry_visible` | boolean | Whether the notification’s expiry is visible to the user. Expiry details are found inside the `message` object. Useful when you want notifications to auto-delete after a certain time.                                     |

**`message` Object Fields**

| Field               | Type    | Description                                                                                                                                                                                                                                                       |
| ------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schema`            | string  | Schema version for the message format.                                                                                                                                                                                                                            |
| `header`            | string  | Main title/header of the notification.                                                                                                                                                                                                                            |
| `text`              | string  | Body text of the notification; supports Markdown and HTML content. Default format is markdown.                                                                                                                                                                    |
| `subtext`           | object  | Secondary text with optional `action_url`. Is visible below the main text. Generally used to show additional information or footer text.                                                                                                                          |
| `avatar`            | object  | Avatar image shown with the notification, generally used to show sender's profile picture; can include an action link.                                                                                                                                            |
| `url`               | string  | URL where user will be redirected when they click the notification.                                                                                                                                                                                               |
| `extra_data`        | string  | JSON string for passing additional data that can be used to design custom notification card UI.                                                                                                                                                                   |
| `tags`              | array   | List of tags to classify the notification (e.g., `feature_launch`, `mentions`). Generally used to filter and organize notifications inside [multiple tabs](/docs/multi-tabs).                                                                                     |
| `is_expiry_enabled` | boolean | Whether expiry handling is enabled for this notification. Expiry is set for notifications that are supposed to auto delete after a certain time (e.g., upcoming maintenances, events, etc.).  Used to send notifications like upcoming maintenances, events, etc. |
| `expiry`            | object  | Expiry configuration (format, type, visibility, value) set when `is_expiry_enabled` is true.                                                                                                                                                                      |
| `is_pinned`         | boolean | Whether this message should pin at top of the notification feed. Generally used to send critical notifications to the user that you don't want to go down when new notifications are added.                                                                       |
| `actions`           | array   | List of call-to-action buttons (each with `url` and `name`).                                                                                                                                                                                                      |

You can understand more about usage and details of message fields in it's [template documentation](/docs/in-app-inbox-template).
