Add a leaderboard
A leaderboard takes two lines in your game code plus one component on your game-over screen.
1. The setup is already done
<GameAuth> is already in your topbar (from npx fgs init). The platform
handles GitHub sign-in. Anonymous players can play but can’t submit.
2. Submit a score when the game ends
import { useLeaderboard } from '@freegamestore/games';
export function Game() { const { submitScore } = useLeaderboard('my-game'); // ← your registry.json id
async function endGame(finalScore: number) { const result = await submitScore(finalScore); if (!result.ok) { // user wasn't signed in, or network failed — score is lost return; } if (result.rank) { // tell them where they landed } }}The gameId (here 'my-game') must match your entry’s id in
registry.json — otherwise the storefront’s detail page won’t show your
scores.
3. Render the list
The pre-built component:
import { Leaderboard, useLeaderboard } from '@freegamestore/games';
function HighScoresPanel() { const lb = useLeaderboard('my-game'); return <Leaderboard {...lb} />;}Two tabs: Top, Recent. Numbered list. Brand-styled. Done.
4. Where to show it
- Pre-game splash — gives a target to beat.
- Game-over screen — by the time it renders,
submitScorehas already resolved and the hook has refreshed itself, so the player’s new entry is in the list.
Score conventions
- Higher = better. If your game scores low-is-good (golf strokes, sudoku
time-to-solve), invert before submitting:
submitScore(BIG_NUMBER - timeSeconds). - Integer scores only. The API accepts a
number; fractional values get stored but the display rounds.
Anonymous play
A player who isn’t signed in can still play your game — but submitScore
returns { ok: false } for them, and no row is created. Standard UX is:
- Game over.
- Show their final score.
- Show the leaderboard.
- If their score would have made the top 10, show “Sign in to save your
score” with the same
<GameAuth>widget pattern.
You can read the auth state with useAuth() to
make that conditional explicit.
When you need a custom view
If a two-tab list doesn’t fit (podium UI, country flags, daily-only,
personal-best ribbon), skip <Leaderboard> and read topScores /
recentScores directly from the hook — render whatever you want.
Related
useLeaderboard— full API.<Leaderboard>— the pre-built list.useAuth— gates writes.