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.
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","..."]
}
]
}
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;
}
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} />
</>
);
}
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.
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.
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
}
[...new Set(games.map(g => g.provider))].