Skip to main content

🚀 Quick Start

Learn how to build your first real-time chat application with XHub Chat in just a few minutes.

Overview

In this guide, you'll learn how to:

  1. Set up the XHub Chat client with Provider
  2. Get Room list and paginate rooms
  3. Show messages with pagination
  4. Send and receive messages in real-time
  5. Handle reactions and threads
Estimated Time

⏱️ This guide takes approximately 10 minutes to complete.

--Note--

  1. Tất cả message được xem là 1 đối tượng event XHubChatEvent. Để lấy thông tin từ message xem các api trong đối tượng XHubChatEvent
  2. Mỗi room sẽ có 1 timeline-set chứa nhiều timeline nhỏ, mỗi timeline sẽ chứa các event (message, reaction, etc..)
  3. Room sẽ có 2 dạng là phòng chat và bài post, mỗi dạng sẽ có UI và tính năng khác nhau tuỳ theo mục đích sử dụng, sử dụng thuộc tính room.roomData.category để phân biệt. Các tính năng like/unlike chỉ thực hiện được trong các phòng là bài Post.
  4. Sử dụng goIntoRoom() trong đối tượng room khi vào phòng để đánh dấu đã đọc, sử dụng goOutRoom() khi rời phòng để cập nhật trạng thái online/offline. Bắt buộc
  5. Khi tải danh sách các phòng thì các tin nhắn trong phòng chưa được load, chỉ có 1 tin nhắn cuối cùng trong phòng, khi nào cần load thêm tin nhắn thì cần gọi hàm paginate trong useTimeline để load tin nhắn trong phòng đó.

Step 1: Install Packages

First, install the required packages:

npm install @xhub-chat/react

Step 2: Create Provider Wrapper

Create a provider component that wraps the SDK provider with your configuration:

src/components/providers/XHubChatProvider.tsx
import type { ICreateClientOpts } from '@xhub-chat/react';
import { XHubChatProvider as XHubChatProviderSDK } from '@xhub-chat/react';

export default function XHubChatProvider({ children }: { children: React.ReactNode }) {
const clientOptions: ICreateClientOpts = {
clientParams: { api_key: 'YOUR_API_KEY' },
baseUrl: 'https://your-server.com/',
realtime: {
channels: ['online:index', `user:YOUR_USER_ID`],
url: 'wss://your-websocket-server.com/connection/websocket',
token: 'YOUR_REALTIME_TOKEN', // <- YOUR_ACCESS_TOKEN
},
userId: 'YOUR_USER_ID',
deviceId: 'YOUR_DEVICE_ID',
accessToken: 'YOUR_ACCESS_TOKEN',
};

return (
<XHubChatProviderSDK
// (Optional) Enable IndexedDB for offline support
workerFactory={indexeddbWorkerFactory}
clientOptions={clientOptions}
startOptions={{ initialSyncLimit: 10 }}
>
{children}
</XHubChatProviderSDK>
);
}
Configuration

See the Configuration Guide for all available options.

Wrap your application with the XHubChatProvider:

src/app/layout.tsx
import XHubChatProvider from '@/components/providers/XHubChatProvider';

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html suppressHydrationWarning>
<body>
<XHubChatProvider>
{children}
</XHubChatProvider>
</body>
</html>
);
}

Step 3: Get Room list and paginate rooms

Create a component to display chat rooms:

src/components/RoomList.tsx
import { useRooms } from '@xhub-chat/react';
import type { Room } from '@xhub-chat/react';

interface RoomListProps {
selectedRoomId: string | null;
onRoomSelect: (roomId: string) => void;
}

export function RoomList({ selectedRoomId, onRoomSelect }: RoomListProps) {
const { rooms, canPaginate, paginate, fetching, isLoading } = useRooms();

if (isLoading) {
return <p>Loading rooms...</p>;
}

return (
<div className="rooms-list">
{rooms.map((room) => (
<button
key={room.roomId}
className={`room-item ${selectedRoomId === room.roomId ? 'selected' : ''}`}
onClick={() => {
onRoomSelect(room.roomId);
// TODO: Mark room as entered
room.goIntoRoom();
}}
>
<div className="room-name">{room.name || room.roomId}</div>
<div className="room-meta">
{room.summary?.getDescription()}
</div>

{/* Unread badge */}
{room.getUnreadNotificationCount() > 0 && (
<div className="unread-badge">
{room.getUnreadNotificationCount()}
</div>
)}
</button>
))}

{/* Load more rooms */}
{canPaginate && (
fetching ? (
<Loader />
) : (
<button onClick={() => paginate(20)}>Load More</button>
)
)}
</div>
);
}
Room Category

Use Room.getCategory() to specialize room rendering based on its type (Chat room or Post). RoomCategory

Step 4: Display Messages with Pagination, Send message in chat room

Show messages from a room with pagination and send new messages:

Attention
  • In Message List, use useTimeline to get messages and pagination functions.
  • With chat room, and message just can send and receive text message. Not support reply and reaction.
src/components/MessageList.tsx
import { useTimeline, Direction } from '@xhub-chat/react';
import type { Room } from '@xhub-chat/react';
import { Message } from './Message';

interface MessageListProps {
room: Room;
}

export function MessageList({ room }: MessageListProps) {
const {
events,
paginate,
sendTextMessage,
canPaginateBackwards,
isPaginatingBackwards,
} = useTimeline({
roomId: room.roomId,
timelineSet: room.getUnfilteredTimelineSet()
});

const handlePaginate = () => {
if (canPaginateBackwards && !isPaginatingBackwards) {
paginate(Direction.Backward, 50, true, 50);
}
};

const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
sendTextMessage(textValue);
setTextValue('');
};

/**
* Messages in room not load when init room,
* need first call to get message in room
*/
useEffect(() => handlePaginate(), []);


return (
<div className="messages-list">
{/* Sort messages by timestamp (newest at bottom) */}
{events
.sort((a, b) => b.getTs() - a.getTs())
.map((event) => (
<Message key={event.id} event={event} />
))}

{/* Load older messages */}
{canPaginateBackwards && (
isPaginatingBackwards ? (
<Loader />
) : (
<button onClick={handlePaginate}>
Load More
</button>
)
)}

<form onSubmit={onSubmit}>
<input
value={textValue}
onChange={e => setTextValue(e.target.value)}
/>
<button type="submit">Send</button>
</form>
</div>
);
}

Step 5: Create Message Component

Build a message component:

src/components/Message.tsx
import type { XHubChatEvent } from '@xhub-chat/react';
import { useXHubChat } from '@xhub-chat/react';

interface MessageProps {
event: XHubChatEvent;
}

export function Message({ event, onReply, onReact, reactions }: MessageProps) {
const { client } = useXHubChat();

// Or can use `client.getUserId()` if you provided userId in provider
const isOwner = event.sender?.userId === `$your-user-id`;

/**
* Get sending status: `SENDING -> SENT -> null`
* - SENDING -> Start call api to server
* - SENT -> Received ack from server (not confirmed by server)
* - `null` -> Send successfully (Common case for normal message)
*/
const isSending = event.getAssociatedStatus() === EventStatus.SENDING;

return (
<div className={`message ${isOwner ? 'message-own' : ''}`}>
{/** Content */}
<div className="message-content">
{event.getContent()?.text || '[No content]'}
</div>
<div className="message-footer">
{/** Timestamp */}
<span className="message-time">
{new Date(event.getTs()).toLocaleString()}
</span>

{/* Sending status */}
{isSending ? <Loader /> : <CheckIcon />}
</div>
</div>
);
}

Step 6: Get contacts and create direct message room

src/components/ContactList.tsx
import { useContacts } from '@xhub-chat/react';

export default function ContactList() {
const { contacts, createDirectRoomWithContact } = useContacts();

return (
<div className="status-card" style={{ width: '20%', height: '100%', overflowY: 'auto' }}>
<h3>Contacts</h3>
{contacts.map(contact => (
<div
onClick={async () => {
const room = await createDirectRoomWithContact(contact.channel_slug);
// Do something with the created room (navigate to it, etc.)
}}
>
<div className="contact-name">{contact.displayName}</div>
<div className="contact-presence" style={{ fontSize: 12, color: '#a7a7a7ff' }}>
{contact.isOnline() ? 'Online' : 'Offline'}
</div>
</div>
))}
</div>
);
}

🎉 Congratulations

You've successfully created a full-featured chat application with XHub Chat! Your app now supports:

  • ✅ Display chat rooms with unread counts
  • ✅ Real-time message updates
  • ✅ Message pagination (load older messages)
  • ✅ Send and receive messages
  • ✅ Reply to messages (threads)
  • ✅ Reactions (like/unlike messages)
  • ✅ Room pagination (load more rooms)

📚 Next Steps

Now that you have a working chat application, you can:

💡 Tips

  • Use goIntoRoom() when entering a room to mark it as read
  • Use goOutRoom() when leaving to update presence
  • Enable workerFactory for better performance with IndexedDB
  • Set initialSyncLimit to control how many rooms load initially
  • Use Direction.Backward for loading older messages
Complete Code

The complete working code is available in the playground app on GitHub.

  • ✅ Show messages in a room
  • ✅ Send new messages
  • ✅ Load more messages with pagination

What's Next?

Now that you have a working chat app, explore more features:

Essential Features

Advanced Topics

API Reference

Need Help?