๐ŸŽฎ Beta Gamer

Chess โ€” React integration guide

Exact files to create, what to put in each one, and how they connect together.

1

Install the SDK

npm install @beta-gamer/react
2

Create a session on your backend

Create this route on your server. It calls Beta Gamer and returns the token to your frontend.

โš ๏ธ Never call the Beta Gamer API directly from your frontend. Your API key must stay server-side only. Use any backend below โ€” a single serverless function is enough.
๐Ÿ“„ server/routes/game.js
app.post('/api/game/start', async (req, res) => {
  const { userId, userName, game, matchType } = req.body;
  const resp = await fetch('https://api.beta-gamer.com/v1/sessions', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer bg_live_xxxx', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      game, matchType, players: [{ id: userId, displayName: userName }],
    }),
  });
  const { sessionToken, sessionId } = await resp.json();
  res.json({ sessionToken, sessionId });
});
3

Create a lobby page that fetches the token

This page calls your backend, gets the token, then redirects to the game page with the token in the URL.

๐Ÿ“„ src/pages/chess/lobby.tsx
import { useState } from 'react';
import { useRouter } from 'next/navigation';   // or react-router: useNavigate

export default function ChessLobby() {
  const router = useRouter();
  const [loading, setLoading] = useState(false);

  const startGame = async (matchType: 'matchmaking' | 'bot') => {
    setLoading(true);
    const res = await fetch('/api/game/chess/start', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ matchType }),
    });
    const { sessionToken, sessionId } = await res.json();

    // Redirect to the game page โ€” pass token in the URL
    router.push(`/chess/game/${sessionId}?token=${sessionToken}`);
  };

  return (
    <div>
      <h1>Play Chess</h1>
      <button onClick={() => startGame('matchmaking')} disabled={loading}>
        Find opponent
      </button>
      <button onClick={() => startGame('bot')} disabled={loading}>
        vs Bot
      </button>
    </div>
  );
}
4

Create the game page โ€” validate token then render

Parse the token from the URL. Validate it first โ€” if the session already ended (e.g. user refreshed), show the result instead of reconnecting.

๐Ÿ“„ src/pages/chess/game/[sessionId].tsx
import { useEffect, useState } from 'react';
import { useParams, useSearchParams } from 'next/navigation';
import { BetaGamerProvider } from '@beta-gamer/react';
import { ChessRoom } from '@/components/chess/ChessRoom';

export default function ChessGamePage() {
  const { sessionId } = useParams<{ sessionId: string }>();
  const searchParams  = useSearchParams();
  const token         = searchParams.get('token');

  const [status, setStatus] = useState<'checking' | 'ok' | 'ended'>('checking');

  useEffect(() => {
    if (!token) { setStatus('ended'); return; }
    fetch(`/api/v1/sessions/validate?token=${token}`)
      .then(r => r.json())
      .then(d => setStatus(d.status === 'ended' ? 'ended' : 'ok'))
      .catch(() => setStatus('ok'));
  }, [token]);

  if (status === 'checking') return <p>Loadingโ€ฆ</p>;

  if (status === 'ended') return (
    <div>
      <p>This session has already ended.</p>
      <a href="/chess/lobby">Play again</a>
    </div>
  );

  return (
    <BetaGamerProvider token={token!} serverUrl="https://api.beta-gamer.com">
      <ChessRoom sessionId={sessionId} onLeave={() => window.location.href = '/chess/lobby'} />
    </BetaGamerProvider>
  );
}
5

Create the ChessRoom component

This component lives inside the provider. It joins matchmaking, listens to events, and renders the board.

๐Ÿ“„ src/components/chess/ChessRoom.tsx
'use client';

import { useEffect, useRef, useState } from 'react';
import { useSocket, useSession } from '@beta-gamer/react';

interface Props {
  sessionId: string;
  onLeave: () => void;
}

export function ChessRoom({ sessionId, onLeave }: Props) {
  const socket     = useSocket();
  const session    = useSession();
  const myPlayerId = session.players[0]?.id ?? '';

  const roomIdRef  = useRef('');
  const [fen, setFen]               = useState('start');
  const [myColor, setMyColor]       = useState<'white' | 'black'>('white');
  const [currentTurn, setCurrentTurn] = useState(0);
  const [players, setPlayers]       = useState<any[]>([]);
  const [playerTimers, setPlayerTimers] = useState<Record<string, number>>({});
  const [gameResult, setGameResult] = useState<{ winner: string | null; reason: string } | null>(null);

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

    const join = () => socket.emit('matchmaking:join', {
      username:  session.players[0]?.displayName,
      playerId:  myPlayerId,
      sessionId,
    });
    if (socket.connected) join(); else socket.once('connect', join);

    socket.on('game:started', (d: any) => {
      roomIdRef.current = d.roomId;
      setMyColor(d.color);
      setFen(d.fen);
      setPlayers(d.players);
      setPlayerTimers(d.playerTimers);
      setCurrentTurn(d.currentTurn);
    });

    socket.on('game:move:made', (d: any) => {
      setFen(d.fen);
      setCurrentTurn(d.currentTurn);
      setPlayerTimers(d.playerTimers);
    });

    socket.on('game:over', (d: any) => {
      setGameResult({ winner: d.winner ?? null, reason: d.reason });
    });

    return () => {
      socket.off('connect', join);
      socket.off('game:started');
      socket.off('game:move:made');
      socket.off('game:over');
    };
  }, [socket]);

  const isMyTurn = players[currentTurn]?.id === myPlayerId;

  const makeMove = (move: string) => {
    if (!isMyTurn || gameResult) return;
    socket?.emit('game:move', { roomId: roomIdRef.current, playerId: myPlayerId, move });
  };

  const resign = () => {
    socket?.emit('game:resign', { roomId: roomIdRef.current, playerId: myPlayerId });
  };

  return (
    <div>
      {/* Render your chess board here using fen, myColor, isMyTurn */}
      {/* Call makeMove('e4') when a piece is moved */}
      <p>FEN: {fen}</p>
      <p>{isMyTurn ? 'Your turn' : "Opponent's turn"}</p>
      {gameResult && <p>Game over: {gameResult.reason} โ€” {gameResult.winner === myPlayerId ? 'You won' : gameResult.winner ? 'You lost' : 'Draw'}</p>}
      <button onClick={resign} disabled={!!gameResult}>Resign</button>
      <button onClick={onLeave}>Leave</button>
    </div>
  );
}
6

Add draw offer support (optional)

Add these listeners inside the same useEffect in ChessRoom.tsx.

const [drawOffered, setDrawOffered] = useState(false);

// Inside useEffect:
socket.on('game:draw:offered', () => setDrawOffered(true));

// In your JSX:
const offerDraw = () => socket?.emit('game:draw:offer', { roomId: roomIdRef.current, playerId: myPlayerId });
const acceptDraw = () => socket?.emit('game:draw:accept', { roomId: roomIdRef.current });

{drawOffered && (
  <div>
    <p>Opponent offered a draw</p>
    <button onClick={acceptDraw}>Accept</button>
    <button onClick={() => setDrawOffered(false)}>Decline</button>
  </div>
)}
Full event payload reference โ†’ Chess socket events
Beta Gamer GaaS API โ€” questions? support@beta-gamer.com