Docs/Guides/Quick Start — React
Guides

Quick start — React

End-to-end: from an API key to a live, running match — no SDK required.

There is an official SDK (@beta-gamer/react) on npm right now, but be honest with yourself before reaching for it: it predates the current hosted-matchmaking architecture and hasn't been updated for it. Installing it today will not give you a working integration. It's being rebuilt in a separate repository and will come back here once it matches what's actually live. Until then, this guide — plain socket.io-client plus about twenty lines of your own React — is the real, working path, not a fallback.
1

Get your API key

Register at beta-gamer.com/register. You'll get a live key (bg_live_…) and a test key (bg_test_…). Use the test key while developing — test sessions are in-memory only and never touch the database, so there's nothing to clean up while you're iterating.

🔒 Never expose your API key on the client. Session creation always happens on your backend — the browser only ever sees the short-lived session token it produces.
2

Install the one real dependency

That's it — the actual Socket.IO client, nothing Beta-Gamer-specific:

bash
npm install socket.io-client
3

Create a session (your backend)

Call POST /v1/sessions from your server, not the browser. Return the resulting sessionToken to your frontend — that token is what proves the browser is allowed to join this specific match, without ever handing it your API key.

js
// e.g. a Next.js API route, or any backend endpoint you control
const res = await fetch('https://api.beta-gamer.com/v1/sessions', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer bg_live_xxxx',   // server only, never sent to the browser
    'Content-Type':  'application/json',
  },
  body: JSON.stringify({
    game:      'checkers',   // checkers | tictactoe | connect4 | fight-arena | pool | ludo
    matchType: 'matchmaking',
    players:   [{ id: user.id, displayName: user.name }],
  }),
});

const { sessionToken, sessionId } = await res.json();
// send sessionToken (not sessionId) down to your React frontend
Prefer to explicitly control who's matched with who, instead of an auto-queue? That's a separate flow — see Hosted Matchmaking. This guide sticks to the simplest path to a first working match.
4

Write a small connection hook

This is the piece an SDK would normally hand you. It's short enough to own yourself, and owning it means you're never blocked waiting on someone else's package. The autoConnect: false and the deliberate absence of an "already connected" guard both matter — see Raw Socket Client for exactly why, it's a real bug this exact pattern was written to avoid, not a stylistic choice.

tsx
// useBetaGamerSocket.ts
import { useEffect, useRef, useState } from 'react';
import { io, Socket } from 'socket.io-client';

export function useBetaGamerSocket(game: string, sessionToken: string | null) {
  const socketRef = useRef<Socket | null>(null);
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    if (!sessionToken) return;

    const socket = io(`https://api.beta-gamer.com/${game}`, {
      auth: { token: sessionToken },
      autoConnect: false,
    });
    socketRef.current = socket;

    socket.on('connect', () => setConnected(true));
    socket.on('disconnect', () => setConnected(false));
    socket.connect();

    return () => {
      socket.disconnect();
      socketRef.current = null;
    };
  }, [game, sessionToken]);

  return { socket: socketRef.current, connected };
}
5

Join matchmaking and listen for the match starting

Once connected, emit matchmaking:join, then listen for game:started. Every game's own page has the exact game:started payload — checkers' is used below as the example.

tsx
import { useEffect, useState } from 'react';
import { useBetaGamerSocket } from './useBetaGamerSocket';

export function CheckersGame({ sessionToken, playerId, displayName }: {
  sessionToken: string; playerId: string; displayName: string;
}) {
  const { socket, connected } = useBetaGamerSocket('checkers', sessionToken);
  const [status, setStatus] = useState('Connecting…');
  const [roomId, setRoomId] = useState('');

  useEffect(() => {
    if (!socket || !connected) return;

    socket.emit('matchmaking:join', { playerId, username: displayName });
    setStatus('Waiting for opponent…');

    socket.on('game:started', ({ roomId: newRoomId, color }) => {
      setRoomId(newRoomId);
      setStatus(`Game started — you're ${color}`);
    });

    socket.on('game:over', ({ winner }) => {
      setStatus(winner === playerId ? 'You won!' : winner ? 'You lost' : 'Draw');
    });

    return () => {
      socket.off('game:started');
      socket.off('game:over');
    };
  }, [socket, connected]);

  return <p>{status}</p>;
}
Notice matchmaking is joined inside the effect that checks connected, not on mount — emitting before the socket has actually finished connecting is a silent no-op, not an error, which makes it a genuinely confusing bug to track down the first time you hit it.
6

Handle the session token on page load

Session tokens are single-use. If someone refreshes a page mid-game or comes back to a finished one, validate first so they see the result instead of the socket trying (and failing) to rejoin a match that's already over.

tsx
useEffect(() => {
  fetch(`https://api.beta-gamer.com/v1/sessions/validate?token=${sessionToken}`)
    .then(r => r.json())
    .then(d => setStatus(d.status === 'ended' ? 'ended' : 'ok'))
    .catch(() => setStatus('ok'));
}, [sessionToken]);
7

Receive webhooks (optional)

When a live-mode game ends, a signed payload lands on your server automatically — useful for updating a leaderboard or awarding rewards without your frontend having to report anything back itself. Full event list and payload shapes: Webhooks.

js
app.post('/webhooks/beta-gamer', (req, res) => {
  const { event, data } = req.body;
  // event: 'session.created' | 'session.started' | 'player.joined' | 'player.left' | 'game.ended'
  res.sendStatus(200);
});
✅ That's a full match, end to end, with nothing installed but the Socket.IO client itself.

Next: pick your game from the Games catalog for its exact events and rules, or read Raw Socket Client in full for reconnection handling and the Strict Mode issue this guide's hook already avoids.