In-App UI
React
Headless UI
Feed

Build your own feed UI (headless)

How to build custom feed UI using our React hooks and client library.

Using our @knocklabs/react and @knocklabs/client libraries, you can create fully custom notification UIs that are backed by the Knock Feed API and real-time service.

In this guide, we'll take a look at creating a completely custom notifications UI in our application in a headless way using Knock's hooks.

Getting started

#

To use this example, you'll need an account on Knock, as well as an in-app feed channel with a workflow that produces in-app feed messages. You'll also need:

  • A public API key for the Knock environment (set as KNOCK_PUBLIC_API_KEY)
  • The channel ID for the in-app feed (set as KNOCK_FEED_CHANNEL_ID)

Installing dependencies

#
Installing dependencies
1npm install @knocklabs/react

Implement KnockProvider

#

First, we'll need to implement the KnockProvider component somewhere in your component tree and authenticate against the Knock API using a user id and API key.

Implement KnockProvider in your app
1import { KnockProvider } from "@knocklabs/react";
2
3const App = ({ user }) => (
4  <KnockProvider apiKey={process.env.KNOCK_PUBLIC_API_KEY} userId={user.id}>
5    <NotificationFeed />
6  </KnockProvider>
7);

Setup the Knock client

#

Next, we'll need to access the instance of the Knock client created by the KnockProvider using the useKnockClient hook.

Access the configured knockClient using useKnockClient
1import { useKnockClient } from "@knocklabs/react";
2
3const NotificationFeed = ({ user }) => {
4  const knockClient = useKnockClient();
5
6  return null;
7};

Setup the Knock feed instance

#

Next, we'll want to set up an instance of a Knock Feed, which will handle the state management and provide a way for us to interact with the messages on the feed.

Create a feed store with Zustand
1import {
2  useKnockClient,
3  useNotifications,
4  useNotificationStore,
5} from "@knocklabs/react";
6import { useEffect } from "react";
7
8const NotificationFeed = ({ user }) => {
9  const knockClient = useKnockClient();
10  const feedClient = useNotifications(
11    knockClient,
12    process.env.KNOCK_FEED_CHANNEL_ID,
13  );
14
15  const { items, metadata } = useNotificationStore(feedClient);
16
17  useEffect(() => {
18    feedClient.fetch();
19  }, [feedClient]);
20
21  return null;
22};

Creating a custom notifications UI

#

The last step is to render our notifications UI using the data that's exposed via the state store (items and metadata).

Render items and metadata in the feed
1import {
2  useKnockClient,
3  useNotifications,
4  useNotificationStore,
5} from "@knocklabs/react";
6import { useEffect } from "react";
7
8const NotificationFeed = ({ user }) => {
9  const knockClient = useKnockClient();
10  const feedClient = useNotifications(
11    knockClient,
12    process.env.KNOCK_FEED_CHANNEL_ID,
13  );
14
15  const { items, metadata } = useNotificationStore(feedClient);
16
17  useEffect(() => {
18    feedClient.fetch();
19  }, [feedClient]);
20
21  return (
22    <div className="notifications">
23      <span>You have {metadata.unread_count} unread items</span>
24
25      {items.map((item) => (
26        <div key={item.id}>
27          <div dangerouslySetInnerHTML={{ __html: item.blocks[0].rendered }} />
28        </div>
29      ))}
30    </div>
31  );
32};

Wrapping up

#

There's a lot more we can do with our notifications UI, but we'll leave that as an exercise to the reader. Here are some examples:

  • Adding mark as read, and archiving behavior to the notification cell
  • Displaying a count of the total number of notifications