How to use Ably with React

The problem with using the Ably client library SDKs in React projects is that the Ably libraries manage their own state and can trigger re-renders. This has historically made it difficult to decide on the best place to put Ably code in your React applications.

To make this easier for developers, Ably includes React extensions as part of the Ably Javascript SDK. This guide hows you how to use Ably React extensions in your React projects (and projects using frameworks based on React, like Next.js).

For more information about the rationale behind Ably React extensions and how they work read this article.

Step 1 – Create your Ably app and API key

To follow this tutorial, you will need an Ably account. Sign up for a free account if you don’t already have one.

Access to the Ably global messaging platform requires an API key for authentication. API keys exist within the context of an Ably application and each application can have multiple API keys so that you can assign different capabilities and manage access to channels and queues.

You can either create a new application for this tutorial, or use an existing one.

To create a new application and generate an API key:

  1. Log in to your Ably account dashboard
  2. Click the “Create New App” button
  3. Give it a name and click “Create app”
  4. Copy your private API key and store it somewhere. You will need it for this tutorial.

To use an existing application and API key:

  1. Select an application from “Your apps” in the dashboard
  2. In the API keys tab, choose an API key to use for this tutorial. The default “Root” API key has full access to capabilities and channels.
  3. Copy the Root API key and store it somewhere. You will need it for this tutorial.

    Copy API Key screenshot

Step 2 – Install Ably React

Ably React extensions are available as part of the Ably Javascript SDK. The SDK is available as an NPM module.

Note: This guide assumes that you are using v1.2.44 or later of the Ably Javascript SDK, and a compatible version of React (v18.1.0 or above).

Install the NPM module into your project as follows:

npm install --save ably

Step 3 – Configure Ably

Once you have installed the Ably NPM module, your first step to using Ably is to create an authorized Ably client. To do this instantiate a new client instance and as part of the instantiation, provide a means to authorize the client. There are several ways to do this including providing a key attribute, but the recommended way is to use the authUrl property to provide a server-side API URL which generates and returns an authorization token.

First, store your Ably API key server-side in a .env file and create an endpoint to handle token requests:

import Ably from "ably/promises";

const rest = new Ably.Rest(process.env.ABLY_API_KEY);

app.get("/ably/auth", (req, res) => {
  const tokenParams = {
    clientId: "my-client-id",
  };
  rest.auth.createTokenRequest(tokenParams, (err, tokenRequest) => {
    if (err) {
      res.status(500).send("Error requesting token: " + JSON.stringify(err));
    } else {
      res.setHeader("Content-Type", "application/json");
      res.send(JSON.stringify(tokenRequest));
    }
  });
});

Next, in your React component add a reference to Ably and the AblyProvider:

import * as Ably from 'ably'
import { AblyProvider } from 'ably/react';

Then, create a new instance of an Ably client and provide that to the AblyProvider:

const client = Ably.Realtime.Promise({ authUrl: '[YOUR_ABLY_TOKEN_API_URL_PATH]'});

return (
  <AblyProvider client={ client }>
  </AblyProvider>
)

Step 4 – Subscribe to a channel

To connect and subscribe to a channel, use the useChannel hook within a child component of AblyProvider:

import { useChannel } from "ably/react";

function Messages() {
  const { channel }  = useChannel("channel-name", (message) => {
      console.log(message);
  });

  return (<></>)
}

Add the new component as a child of AblyProvider

const client = Ably.Realtime.Promise({ authUrl: '[YOUR_ABLY_TOKEN_API_URL_PATH]'});

return (
  <AblyProvider client={ client }>
    <Messages />
  </AblyProvider>
)

You can combine useChannel with a React useState hook. For example, if you want to keep a list of messages in your app state and update the state when new messages arrive on the channel:

const [messages, updateMessages] = useState([]);
const {channel} = useChannel("channel-name", (message) => {
    updateMessages((prev) => [...prev, message]);
});

// Convert the messages to list items to render in a React component
const messagePreviews = messages.map((msg, index) => <li key={index}>{msg.data.someProperty}</li>);

The useChannel hook supports all the same parameters that the client library SDK does. So you can also use features like rewind:

const {channel} = useChannel("[?rewind=100]your-channel-name", (message) => {
    // List the last 100 messages on the channel
    console.log(message);
});

Step 5 – Publish and other channel operations

The call to useChannel returns the channel instance. You can use this channel instance to publish a message to that channel:

channel.publish("test-message", { text: "message text" });

The channel instance also supports all the same functions that the client library SDK does. So you can use features like retrieving channel history:

const history = channel.history((err, result) => {
    const lastMessage = resultPage.items[0];
    console.log('Last message: ' + lastMessage.id + ' - ' + lastMessage.data);
});

Step 6 – List present members

The usePresence hook lets you subscribe to presence messages on a channel, so that you can be notified when members enter or leave the channel and retrieve the current presence set.

Retrieving the Presence set

To use the Presence capability, you need to assign each client a unique clientId. In a production application, this clientId should be returned by your token request endpoint (see Step 3). But you can also do this when you create your client instance:

const client = Ably.Realtime.Promise({ authUrl: '[YOUR_ABLY_TOKEN_API_URL_PATH]', clientId: '[YOUR_CLIENT_ID]' });

You can then import and use the usePresence hook. It returns an array of standard Ably PresenceMessage objects:

import { AblyProvider, useChannel, usePresence } from "ably/react";

const [presenceData] = usePresence("channel-name", "optional initial state");

// Convert presence data to list items to render
const members = presenceData.map((msg, index) => <li key={index}>{msg.clientId}: {msg.data}</li>);

Updating the status of a channel member

You can update presence data using the updateStatus function:

const [presenceData, updateStatus] = usePresence("your-channel-name", "optional initial state");

updateStatus("new status");

The new state will be sent to the channel, and any other clients subscribed to the channel will be notified of the change immediately.

If you don’t want to use the presence data returned from usePresence, you can configure a callback:

const [_, updateStatus] = usePresence("channel-name", "optional initial state", (presenceUpdate) => {
    console.log(presenceUpdate);
});

The usePresence hook supports objects, as well as strings:

usePresence("your-channel-name", { someProperty: "someValue" });

Using type hints (TypeScript only)

If you are using TypeScript, there are type hints to ensure that presence updates are of the same type as your initial constraint, or a provided generic type parameter. In this example, the MyPresenceType is checked. If it is omitted, the shape of the initial value will be used. If that’s omitted, the default will be of type any:

const TypedUsePresenceComponent = () => {
    const [val] = usePresence<MyPresenceType>("testChannelName", { foo: "bar" });

    return (
        <div role='presence'>
            {JSON.stringify(val)}
        </div>
    );
}

interface MyPresenceType {
    foo: string;
}

Next Steps

You might find the following resources helpful: