React React (JavaScript/TypeScript SDK)

⚠️

Colyseus aims to provide a tighter integration with React in the future. If you’d like to contribute to this effort, please check this thread on Discord.

The objective is to iterate on @pedr0fontoura’s approach from his use-colyseus project.

Raw / Simple Usage

The simplest way to use Colyseus in React is by using the useEffect hook to join a room and handle its events. Make sure to leave the room when the component is unmounted.

RoomComponent.tsx
import { useEffect } from "react";
import { Client, Room } from "colyseus.js";
 
const client = new Client("http://localhost:2567");
 
function RoomComponent () {
	const roomRef = useRef<Room>();
 
    const [ isConnecting, setIsConnecting ] = useState(true);
    const [ players, setPlayers ] = useState([]);
 
	useEffect(() => {
		const req = client.joinOrCreate("my_room", {});
 
		req.then((room) => {
			roomRef.current = room;
 
			setIsConnecting(false);
 
            // handle room events here
			room.onStateChange((state) => setPlayers(state.players.toJSON()));
		});
 
		return () => {
            // make sure to leave the room when the component is unmounted
			req.then((room) => room.leave());
		};
	}, []);
 
    return (
        <div>
            {players.map((player) => (
                <div key={player.id}>{player.name}</div>
            ))}
        </div>
    );
}

Using a Context Provider

TODO: Add a more complex example using a React Context Provider.

Alternatively, you can use a React Context Provider to manage the connection and room state across your application.

RoomContext.tsx
import React, { createContext, useContext } from 'react';
import { Room } from 'colyseus.js';
 
interface RoomContextType {
    isConnecting: boolean;
    isConnected: boolean;
    room: Room;
    join: () => void;
    joinError: boolean;
    state: any; // replace `any` with your state type
}
 
export const RoomContext = createContext<RoomContextType>({});
 
export function useRoom() { return useContext(RoomContext); }
 
let room!: Room;
 
//
// Workaround for React.StrictMode, to avoid multiple join requests
//
let hasActiveJoinRequest: boolean = false;
 
export function RoomProvider({ children }: { children: React.ReactNode }) {
    const [searchParams, _] = useSearchParams();
 
    const [joinError, setJoinError] = React.useState(false);
    const [isConnecting, setIsConnecting] = React.useState(false);
    const [isConnected, setIsConnected] = React.useState(false);
    const [state, setState] = React.useState<ReturnType<BalootiState['toJSON']>>(undefined)
 
    const join = () => {
        if (hasActiveJoinRequest) { return; }
        hasActiveJoinRequest = true;
 
        setIsConnecting(true);
 
        try {
            room = await client.joinOrCreate("my_room");
 
        } catch (e) {
            setJoinError(true);
            setIsConnecting(false);
            return;
 
        } finally {
            hasActiveJoinRequest = false;
        }
 
        //
        // cache reconnection token, if user goes back to this URL, we can try re-connect to the room.
        // TODO: do not cache reconnection token if user is spectating
        //
        localStorage.setItem("reconnection", JSON.stringify({
            token: room.reconnectionToken,
            roomId: room.roomId,
        }));
 
        room.onStateChange((state) => setState(state.toJSON()));
        room.onLeave(() => setIsConnected(false));
 
        setIsConnected(true);
    };
 
    return (
        <RoomContext.Provider value={{ isConnecting, isConnected, room, join, joinError, state }}>
            {children}
        </RoomContext.Provider>
    );
}
Last updated on