Getting started: Chat UI Components with React

This guide shows you how to add Ably Chat React UI Components to a brand-new React app built with Vite.

You will:

  • Create an Ably realtime client and Chat client
  • Install the ready-made UI component package
  • Build up a complete chat interface component by component
  • Customise look and feel with your own styles
  • Add your own chat settings and avatars

Prerequisites

  1. Sign up for an Ably account.

  2. Create an app and copy its API key (the root key is fine while you experiment).

  3. Install the Ably CLI:

npm install -g @ably/cli
  1. Log in and set the default app/key:
ably login
ably apps switch
ably auth keys switch

Create a new project

  1. Scaffold a React + TypeScript project with Vite:
npm create vite@latest my-chat-ui-app -- --template react-ts
cd my-chat-ui-app
  1. (Optional) Add Tailwind CSS if you want utility classes elsewhere in your app. The UI kit itself ships pre-compiled CSS, so Tailwind isn't required:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Follow the Tailwind Vite guide to configure the generated files.

  1. Install the Chat UI kit and its peers:
npm install ably @ably/chat @ably/chat-react-ui-components

# Ensure React 19 is present (Vite template already has these)
npm install react react-dom

That's all the setup you need—the kit's CSS is automatically bundled by Vite alongside the rest of your styles.

Usage

The components and their stylesheet can be imported into your React applications like so:

React

1

2

3

// App.tsx
import { App, ChatWindow, Sidebar, RoomInfo, AppLayout } from '@ably/chat-react-ui-components';
import '@ably/chat-react-ui-components/dist/styles.css';

The @ably/chat-react-ui-components/dist/styles.css file contains all the necessary styles for the components, compiled from the Tailwind utility classes used in the component code.

Providers setup

ably-chat-react-ui-components relies on the same React context providers as the underlying Chat SDK, plus a few extras for theming and avatars.

Basic provider setup

In your main entry file (e.g. main.tsx), replace the content with the following code to set up the providers:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

// main.tsx
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';
import { ChatClientProvider } from '@ably/chat/react';
import { ThemeProvider, AvatarProvider, ChatSettingsProvider } from '@ably/chat-react-ui-components';
import '@ably/chat-react-ui-components/dist/style.css';

// Create Ably Realtime client
const ablyClient = new Ably.Realtime({
  key: '{{API_KEY}}', // Replace with your Ably API key
  clientId: 'your-chat-client-id',
});

const chatClient = new ChatClient(ablyClient);

ReactDOM.createRoot(document.querySelector('#root') || document.createElement('div')).render(
  <React.StrictMode>
    <ThemeProvider>
      <AvatarProvider>
        <ChatSettingsProvider>
          <ChatClientProvider client={chatClient}>
            {/* Your components will go here */}
          </ChatClientProvider>
        </ChatSettingsProvider>
      </AvatarProvider>
    </ThemeProvider>
  </React.StrictMode>
);

The components you make throughout this guide will be wrapped in these providers to ensure they have access to the necessary context.

Provider responsibilities

  • ChatClientProvider – supplies the Ably Chat client so child components can publish, subscribe, and manage rooms
  • ThemeProvider – toggles light/dark mode and optionally stores the preference (persist: true)
  • AvatarProvider – generates and caches user / room avatars
  • ChatSettingsProvider – provides a place to store and retrieve chat settings that control the UI, such as enabling message editing or reactions
  • ChatRoomProvider – provides the chat room context for components that operate on a specific room, such as the ChatWindow, TypingIndicators and RoomInfo components

Building your chat interface

Let's build up a complete chat interface step by step, starting with individual components and working up to a full application.

ChatWindow component

The ChatWindow component provides the main chat interface for a room. It handles everything related to messages - display, editing, deletion, reactions, history and real-time updates.

Features:

  • Message display with history loading and infinite scroll
  • Message editing, deletion, and reactions
  • Typing indicators and presence awareness
  • Custom header and footer content areas
  • Discontinuity recovery on reconnection
  • Configurable message window size for performance

Requirements: Must be wrapped in ChatRoomProvider, AvatarProvider, and ChatSettingsProvider. The settings provider controls which message actions (update/delete/react) are available.

Create a simple file called App.tsx with the following content:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

import { ChatWindow } from '@ably/chat-react-ui-components';
import { ChatRoomProvider } from '@ably/chat/react';

export function ChatApp() {
  return (
    <div className="bg-gray-50 rounded-lg border">
      <div
        className="overflow-hidden border rounded-lg flex"
        style={{ width: '70vw', height: '70vh' }}
      >
        <ChatRoomProvider name="my-first-room">
          <ChatWindow roomName="my-first-room" enableTypingIndicators={true} />
        </ChatRoomProvider>
      </div>
    </div>
  );
}

Now import the ChatApp component in your main.tsx file and render it within the providers:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';
import { ChatClientProvider } from '@ably/chat/react';
import { ThemeProvider, AvatarProvider, ChatSettingsProvider } from '@ably/chat-react-ui-components';
import '@ably/chat-react-ui-components/dist/style.css';
import { ChatApp } from './App.tsx'; // Assuming your app.tsx is in the same directory

const ablyClient = new Ably.Realtime({
  key: '{{API_KEY}}', // Replace with your Ably API key
  clientId: 'your-chat-client-id',
});

const chatClient = new ChatClient(ablyClient);

ReactDOM.createRoot(document.querySelector('#root') || document.createElement('div')).render(
  <React.StrictMode>
    <ThemeProvider>
      <AvatarProvider>
        <ChatSettingsProvider>
          <ChatClientProvider client={chatClient}>
            <ChatApp />
          </ChatClientProvider>
        </ChatSettingsProvider>
      </AvatarProvider>
    </ThemeProvider>
  </React.StrictMode>
);

Typing indicators are enabled by default, but you can disable them by setting enableTypingIndicators={false}.

Try sending a message to the room, use the following command:

ably rooms messages send my-first-room "Hello, world!" --clientId your-chat-client-id

To simulate typing in the room, use the following command:

ably rooms typing keystroke my-first-room --clientId your-chat-client-id

Adding Room Information

The RoomInfo component displays comprehensive information about a chat room. It shows the room avatar, live presence count, participant details, and typing indicators.

Features:

  • Room avatar display with automatic generation
  • Live presence count badge showing active participants
  • Interactive hover tooltip with participant preview
  • Expandable participant list with detailed user information
  • In-place avatar with color and image customization via the AvatarProvider
  • Typing indicators built-in
  • Accessibility support with ARIA roles and keyboard navigation

Requirements: Must be wrapped in both ChatRoomProvider and AvatarProvider.

Let's add the RoomInfo component to our chat window's header section, replace the content in App.tsx with the following code:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

// App.tsx
import { ChatWindow, RoomInfo } from '@ably/chat-react-ui-components';
import { ChatRoomProvider } from '@ably/chat/react';

export function ChatApp() {
  return (
    <div className="bg-gray-50 rounded-lg border">
      <div
        className="overflow-hidden border rounded-lg flex"
        style={{ width: '70vw', height: '70vh' }}>
        <ChatRoomProvider name="my-first-room">
          <ChatWindow
            roomName="my-first-room"
            customHeaderContent={<RoomInfo />}
          />
        </ChatRoomProvider>
      </div>
    </div>
  );
}

The RoomInfo component will automatically display the room avatar, participant count, and typing indicators. You can customize its appearance by passing props like roomAvatar, position, and className.

React

1

2

3

4

5

6

7

8

9

10

11

12

13

<RoomInfo
  // Optional custom avatar data (overrides AvatarProvider)
  roomAvatar={{
    displayName: 'VIP Lounge',
    color: 'bg-green-500',
    initials: 'VL',
    src: 'https://example.com/vip-avatar.jpg',
  }}
  // Position for dropdown menus
  position={{ top: 60, left: 250 }}
  // Custom styling
  className="p-4 bg-blue-400 rounded-lg shadow-sm"
/>

For now we will just use the defaults for the RoomInfo component.

Try using the Ably CLI to enter the rooms presence set, you should see this reflected in the RoomInfo component:

ably rooms presence enter my-first-room --clientId your-chat-client-id

You can also use the CLI to simulate typing in the room too, which will show up in the RoomInfo component's typing indicators:

ably rooms typing keystroke my-first-room --clientId your-chat-client-id

Adding Room Reactions

The chat window also supports custom footer content, which can be used for additional controls like reactions. You can pass any React component as customFooterContent, e.g., the RoomReaction component.

To add the RoomReaction component, update your App.tsx file as follows:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// App.tsx
import { ChatWindow, RoomInfo, RoomReaction } from '@ably/chat-react-ui-components';
import { ChatRoomProvider } from '@ably/chat/react';

export function ChatApp() {
  return (
    <div className="bg-gray-50 rounded-lg border">
      <div
        className="overflow-hidden border rounded-lg flex"
        style={{ width: '70vw', height: '70vh' }}>
        <ChatRoomProvider name="my-first-room">
          <ChatWindow
            roomName="my-first-room"
            customHeaderContent={<RoomInfo />}
            customFooterContent={<RoomReaction />}
          />
        </ChatRoomProvider>
      </div>
    </div>
  );
}

Now try sending a thumbs up reaction to the room using the Ably CLI:

ably rooms reactions send my-first-room 👍 --clientId your-chat-client-id

You should see the reaction appear on the screen as a small explosion animation.

Adding a Sidebar

Now let's add a Sidebar component to our chat application. The Sidebar provides room navigation and management, allowing users to create, join, and leave rooms. It can be placed next to the ChatWindow to provide a complete chat interface.

Features:

  • Collapsible interface with avatar-only mode
  • Room creation and management
  • Theme toggle integration
  • Active room highlighting
  • Room count display
  • Automatic room attachment/detachment

The sidebar automatically manages room connections as users navigate. It also uses occupancy events to provide context about connected and present users in each room.

To add the Sidebar component, update your App.tsx file as follows:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

// App.tsx
import { ChatWindow, RoomInfo, RoomReaction, Sidebar } from '@ably/chat-react-ui-components';
import { ChatRoomProvider } from '@ably/chat/react';
import { type RoomOptions } from '@ably/chat';
import { useState, useCallback } from 'react';

const DEFAULT_ROOM_OPTIONS: RoomOptions = {
  occupancy: { enableEvents: true },
};

export function ChatApp() {
  const [roomNames, setRoomNames] = useState<string[]>(['my-first-room', 'general', 'random']);
  const [activeRoom, setActiveRoom] = useState<string | undefined>('my-first-room');
  const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);

  const addRoom = useCallback((name: string) => {
    setRoomNames((prev) => (prev.includes(name) ? prev : [...prev, name]));
    setActiveRoom(name);
  }, []);

  const leaveRoom = useCallback(
    (name: string) => {
      setRoomNames((prev) => {
        const next = prev.filter((n) => n !== name);
        if (next.length === 0) {
          setActiveRoom('');
        } else if (name === activeRoom) {
          setActiveRoom(next[0]);
        }
        return next;
      });
    },
    [activeRoom],
  );

  return (
    <div className="bg-gray-50 rounded-lg border">
      <div className="overflow-hidden border rounded-lg flex" style={{width: '70vw', height: '70vh'}}>
        {/* Sidebar */}
        <div className={`flex-shrink-0 ${isSidebarCollapsed ? 'w-16' : 'w-64'}`}>
          <Sidebar
            roomNames={roomNames}
            activeRoomName={activeRoom}
            addRoom={addRoom}
            defaultRoomOptions={DEFAULT_ROOM_OPTIONS}
            setActiveRoom={setActiveRoom}
            leaveRoom={leaveRoom}
            isCollapsed={isSidebarCollapsed}
            onToggleCollapse={() => setIsSidebarCollapsed(prev => !prev)}
          />
        </div>

        {/* Chat Window */}
        <div className="flex-1 overflow-hidden">
          {activeRoom ? (
            <ChatRoomProvider
              key={activeRoom}
              name={activeRoom}
              attach={false}
              release={false}
              options={DEFAULT_ROOM_OPTIONS}
            >
              <ChatWindow
                roomName={activeRoom}
                customHeaderContent={<RoomInfo />}
                customFooterContent={<RoomReaction />}
              />
            </ChatRoomProvider>
          ) : (
            <div className="flex items-center justify-center h-full">
              <p>Select a room to start chatting</p>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Now we have a complete chat interface with a sidebar for room navigation and a chat window for messaging.

App component

Finally, if you prefer a ready-to-use solution, the library provides an App component that combines all the components above with sensible defaults. This is great if you need to prototype quickly or see how everything fits together.

Features:

  • Manages room state (adding, leaving, selecting rooms)
  • Shows loading state when not connected
  • Renders layout with Sidebar and ChatWindow
  • Includes RoomInfo in header and RoomReaction in footer
  • Handles connection status and error states

To use the App component, import it in your main.tsx file and render it within the providers:

React

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

// main.tsx
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';
import { ChatClientProvider } from '@ably/chat/react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import {
  App,
  ThemeProvider,
  AvatarProvider,
  ChatSettingsProvider
} from '@ably/chat-react-ui-components';
import '@ably/chat-react-ui-components/dist/style.css';

// Create Ably Realtime client
const ablyClient = new Ably.Realtime({
  key: '{{API_KEY}}',
  clientId: 'your-chat-client-id',
});

const chatClient = new ChatClient(ablyClient);

ReactDOM.createRoot(document.querySelector('#root') || document.createElement('div')).render(
  <React.StrictMode>
    <ThemeProvider options={{ persist: true, defaultTheme: 'light' }}>
      <AvatarProvider>
        <ChatSettingsProvider>
          <ChatClientProvider client={chatClient}>
            <App initialRoomNames={"my-first-room"}/>
          </ChatClientProvider>
        </ChatSettingsProvider>
      </AvatarProvider>
    </ThemeProvider>
  </React.StrictMode>
);

You should now see a fully functional chat application with a sidebar, chat window, room info, and reactions and an initial room already created.

Summary

The components are designed to be flexible and composable, allowing you to mix and match them as needed. You can also customize appearance and behavior using the context providers like ThemeProvider, AvatarProvider, and ChatSettingsProvider.

You can use the individual components like RoomInfo, ChatWindow, and Sidebar to create custom layouts and functionality, or you can use the complete App component for rapid prototyping of a full chat application.

The UI components are experimental and are highly likely to change in future releases.

Next steps

Continue exploring Ably Chat UI components:

Explore the Ably CLI further, or check out the Chat JS API references for additional functionality.

Select...