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, we have built some custom React hooks. This guide hows you how to use Ably React Hooks in your React projects (and projects using frameworks based on React, like Next.js).

For more information about the rationale behind Ably React Hooks 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 Hooks

Note: This guide assumes that you are using v2.0 or later of Ably React Hooks, and a compatible version of React (v18.1.0 or above).

Ably React Hooks are available as an NPM module. The module works out of the box with the create-react-app toolchain.

Install the NPM module into your project as follows:

npm install --save @ably-labs/react-hooks

Step 3 – Configure Ably

Once you have installed the Ably React Hooks NPM module, you need to configure Ably with the API key you generated in Step 1.

First, add a reference to the hooks in your React code:

import { configureAbly } from "@ably-labs/react-hooks";

Then, call the configureAbly function to create an instance of the Ably client library SDK:

configureAbly({ key: "your-ably-api-key"});

configureAbly matches the method signature of the Ably client library SDK and requires either a string or an AblyClientOptions object.

In a production application, you should use token authentication to protect your API key from being compromised. One way to achieve this is to store your 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));
    }
  });
});

You can then use configureAbly with an authUrl so that your client will request tokens automatically:

configureAbly({ authUrl: "/ably/auth" });

Step 4 – Subscribe to a channel

To connect and subscribe to a channel, use the useChannel hook:

import { configureAbly, useChannel } from "@ably-labs/react-hooks";

const [channel, ably] = useChannel("channel-name", (message) => {
    console.log(message);
});

The call to useChannel returns the channel instance and also a reference to the Ably client library SDK.

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>);

Step 5 – Publish and other channel operations

You can use the channel instance returned by useChannel to publish a message to that channel:

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

Because you also have access to the Ably client library SDK from your call to useChannel, you can perform any other operations on the channel. For example, retrieving channel history:

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

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 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 in the call to configureAbly:

configureAbly({ key: "your-ably-api-key", clientId: generateRandomId() });

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

import { configureAbly, useChannel, usePresence } from "@ably-labs/react-hooks";

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: