HomeBlog › Building a Slot Game Lobby in React (with a Casino

Building a Slot Game Lobby in React (with a Casino API)

Tutorial 27 May 2026 · 9 min read

A slot lobby is a deceptively simple UI — a grid of thumbnails with filters by provider, vertical, and search. Done wrong it re-renders 1,400 cards every keystroke. Done right, it feels instant.

The data shape

Every casino aggregator returns games as a flat list with metadata. With BigBang's /games endpoint:

{
  "games": [
    {
      "code": "vs20fruitsw",
      "name": "Sweet Bonanza",
      "provider": "pragmatic-play",
      "vertical": "slot",
      "thumb": "https://cdn.bigbang.../sweet-bonanza.jpg",
      "rtp": 96.51,
      "volatility": "high",
      "languages": ["en","de","es","..."],
      "currencies": ["EUR","USD","BTC","..."]
    }
  ]
}

1. Load once, filter in memory

Don't hit /games on every filter change. Load once on mount, filter client-side. 1,400 games is ~250KB JSON — fits comfortably in browser memory.

import { useEffect, useState, useMemo } from 'react';

function useGames() {
  const [games, setGames] = useState([]);
  useEffect(() => {
    fetch('https://api.bigbangcasino.bet/games', {
      headers: { 'X-API-Key': import.meta.env.VITE_BB_KEY }
    })
    .then(r => r.json())
    .then(d => setGames(d.games));
  }, []);
  return games;
}

2. Filter pipeline with useMemo

function Lobby() {
  const games = useGames();
  const [search, setSearch] = useState('');
  const [provider, setProvider] = useState('all');

  const filtered = useMemo(() => {
    const q = search.toLowerCase();
    return games.filter(g =>
      (provider === 'all' || g.provider === provider) &&
      (q === '' || g.name.toLowerCase().includes(q))
    );
  }, [games, search, provider]);

  return (
    <>
      <Filters provider={provider} setProvider={setProvider} setSearch={setSearch} />
      <Grid games={filtered} />
    </>
  );
}

3. Memoized game card — the critical optimization

const GameCard = React.memo(function GameCard({ game, onLaunch }) {
  return (
    <div className="card" onClick={() => onLaunch(game.code)}>
      <img src={game.thumb} loading="lazy" alt={game.name} />
      <div>{game.name}</div>
      <small>{game.provider}</small>
    </div>
  );
});

Without React.memo, every keystroke re-renders every card. With it, only cards that enter or leave the filtered set get rendered. CPU goes from 80% to 5% on the same hardware.

4. Lazy-load images, not data

Use loading="lazy" on the <img> — browsers only fetch thumbnails as they scroll into view. No JS-based virtualization needed for <2000 items.

For >2000 items, switch to react-window or react-virtuoso. Below that, the browser's intersection observer is enough.

5. Game launch flow

async function onLaunch(gameCode) {
  const r = await fetch('https://api.bigbangcasino.bet/launch', {
    method: 'POST',
    headers: {
      'X-API-Key': import.meta.env.VITE_BB_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      game_code: gameCode,
      player_id: currentPlayerId,
      currency: currentCurrency,
      lang: currentLang,
      return_url: window.location.origin + '/lobby'
    })
  });
  const { launch_url } = await r.json();
  window.location = launch_url;  // or open in iframe
}

6. Things that go wrong in production

Get a sandbox key Browse the catalog