Skip to main content
Version: Next

Text Chat

This guide shows how to implement text chat in your application using data channels. Data channels allow you to send and receive arbitrary binary data between peers in a room.

Prerequisites

Before implementing text chat, you must be connected to a room. Data channels only work after you have successfully joined a room.

tip

For a deeper understanding of how data channels work, including channel types and broadcast behavior, see Data Channels.


Step 1 — Set up the chat hook

Use the useDataChannel hook to work with data channels. The typical flow is:

  1. Initialize the data channel after connecting to a room
  2. Subscribe to incoming messages
  3. Publish messages to other peers
import { useConnection, useDataChannel } from "@fishjam-cloud/react-client"; import { useCallback, useEffect, useState } from "react"; export function useChat() { const { peerStatus } = useConnection(); const { initializeDataChannel, publishData, subscribeData, dataChannelReady, } = useDataChannel(); const [messages, setMessages] = useState<string[]>([]); // Step 1: Initialize data channel when connected useEffect(() => { if (peerStatus === "connected") { initializeDataChannel(); } }, [peerStatus, initializeDataChannel]); // Step 2: Subscribe to incoming messages useEffect(() => { if (!dataChannelReady) return; const unsubscribe = subscribeData( (data: Uint8Array) => { const message = new TextDecoder().decode(data); setMessages((prev) => [...prev, message]); }, { reliable: true }, ); return unsubscribe; }, [subscribeData, dataChannelReady]); // Step 3: Publish messages const sendMessage = useCallback( (text: string) => { if (!dataChannelReady) return; const encoded = new TextEncoder().encode(text); publishData(encoded, { reliable: true }); }, [publishData, dataChannelReady], ); return { messages, sendMessage, ready: dataChannelReady }; }

Step 2 — Use the chat hook in your component

Once you have the hook, you can use it in any component to send and display messages:

function ChatPanel() { const { messages, sendMessage, ready } = useChat(); const [input, setInput] = useState(""); const handleSend = () => { if (input.trim()) { sendMessage(input); setInput(""); } }; if (!ready) { return <div>Connecting to chat...</div>; } return ( <div> <div> {messages.map((msg: string, i: number) => ( <div key={i}>{msg}</div> ))} </div> <input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleSend()} /> <button onClick={handleSend}>Send</button> </div> ); }

Why use the reliable channel?

For chat messages, we use { reliable: true } because:

  • Ordered delivery: Messages arrive in the order they were sent
  • Guaranteed delivery: All messages will be delivered, with automatic retransmission if needed

This ensures no chat messages are lost or arrive out of order.


See also