How to Build a Fantasy World Cup 2026 App in 1 Hour
Build a Fantasy World Cup 2026 app from scratch with React and the FIFA World Cup API. Squad selection, captain picks, points scoring, and leaderboards.
Every World Cup spawns a fresh wave of fantasy football products: Sky Super 6, ESPN Fantasy World Cup, official FIFA games, plus thousands of small private leagues built by developers like you. They all share the same data needs — squads, lineups, goals, assists, minutes — and most are buildable in a single weekend.
This tutorial gets you to a working Fantasy World Cup 2026 app in roughly one hour of focused coding, using TheStatsAPI for the data and Next.js + Supabase for the front-end and storage.
The data model
Fantasy football is simple in shape:
- Players — every player in the World Cup, with position and price
- Squads — each user picks 15 players within a £100m budget
- Lineups — for each gameweek (match-day) the user picks a starting 11 + captain
- Scores — calculated from real match events: goals, assists, clean sheets, minutes, cards
- Leaderboards — sum of weekly scores per user
We'll skip the budget logic for simplicity but the points scoring is the same.
Step 1: Get the player list
The 48 World Cup squads collectively contain around 1,300 players (26-player squads × 48 + replacements). Fetch them once at build time:
async function fetchWorldCupPlayers() {
const r = await fetch(
'https://api.thestatsapi.com/api/football/players?competition_id={COMPETITION_ID}&season=2026&per_page=2000',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
return (await r.json()).data;
}
Cache this aggressively — squads are largely finalised before kickoff and minor replacements during the tournament can be picked up by a daily background job.
Step 2: Render the squad selection UI
A simple table with filters for position, team, and price:
function PlayerPicker({ players, squad, onAdd, onRemove }) {
const [position, setPosition] = useState('all');
const [team, setTeam] = useState('all');
const filtered = players.filter((p) =>
(position === 'all' || p.position === position) &&
(team === 'all' || p.team.slug === team)
);
return (
<table>
<thead>
<tr><th>Name</th><th>Team</th><th>Pos</th><th>Price</th><th></th></tr>
</thead>
<tbody>
{filtered.map((p) => (
<tr key={p.id}>
<td>{p.name}</td>
<td>{p.team.name}</td>
<td>{p.position}</td>
<td>£{p.price}m</td>
<td>
{squad.find((s) => s.id === p.id)
? <button onClick={() => onRemove(p.id)}>Remove</button>
: <button onClick={() => onAdd(p.id)}>Add</button>}
</td>
</tr>
))}
</tbody>
</table>
);
}
For a real product, add image avatars, recent form, fixtures-difficulty colour coding.
Step 3: Define the scoring rules
Standard FPL-style rules adapted for a 3-week tournament:
const SCORING = {
appearance_60min: 2,
appearance_under_60min: 1,
goal_goalkeeper: 6,
goal_defender: 6,
goal_midfielder: 5,
goal_forward: 4,
assist: 3,
clean_sheet_defender: 4,
clean_sheet_goalkeeper: 4,
clean_sheet_midfielder: 1,
penalty_save: 5,
penalty_miss: -2,
yellow_card: -1,
red_card: -3,
own_goal: -2,
};
Tweak as needed — your league, your rules.
Step 4: Score a player from real match data
After each match, fetch the player stats and compute fantasy points:
async function scorePlayer(playerId, matchId) {
const r = await fetch(
`https://api.thestatsapi.com/api/football/matches/${matchId}/players/${playerId}/stats`,
{ headers }
);
const stats = (await r.json()).data;
let points = 0;
if (stats.minutes >= 60) points += SCORING.appearance_60min;
else if (stats.minutes > 0) points += SCORING.appearance_under_60min;
points += stats.goals * SCORING[`goal_${stats.position}`];
points += stats.assists * SCORING.assist;
if (stats.clean_sheet) points += SCORING[`clean_sheet_${stats.position}`];
points += stats.yellow_cards * SCORING.yellow_card;
points += stats.red_cards * SCORING.red_card;
return points;
}
Step 5: Captain doubles
A captain scores 2× points. Pick wisely:
function captainBonus(lineup, results) {
const captain = lineup.find((p) => p.isCaptain);
return scorePlayer(captain.id, results.matchId); // adds another full point set
}
Step 6: Persist to Supabase
// Save a user's squad
await supabase.from('fantasy_squads').upsert({
user_id: user.id,
player_ids: squad.map((p) => p.id),
captain_id: captain.id,
vice_captain_id: viceCaptain.id,
gameweek: currentGameweek,
});
// Read leaderboard
const { data } = await supabase
.from('fantasy_scores')
.select('user_id, total_points')
.order('total_points', { ascending: false })
.limit(100);
Step 7: Background scoring job
Run after every match finishes (or every hour during match-days):
// pages/api/score-matches.ts
export default async function handler(req, res) {
const finished = await fetch(
'https://api.thestatsapi.com/api/football/matches?competition_id={COMPETITION_ID}&status=finished&since=' + lastRun,
{ headers }
).then((r) => r.json());
for (const match of finished.data) {
const squads = await supabase.from('fantasy_squads').select('*').eq('gameweek', match.gameweek);
for (const squad of squads.data) {
const points = await scoreSquad(squad, match);
await supabase.from('fantasy_scores').upsert({
user_id: squad.user_id, gameweek: match.gameweek, points,
});
}
}
res.status(200).end();
}
Trigger via Vercel cron, GitHub Actions, or a simple webhook from your API provider.
Polish ideas
What separates a hobby project from something fans actually use:
- Live points — show fantasy points updating during matches (poll player stats every 60 seconds)
- Differentials — highlight captain picks that <5% of the league chose
- Transfers — allow 2 free transfers between match-days
- Wildcards — one-off ability to swap your entire squad
- Mini leagues — friends create a private league with a join code
- Push notifications — "your captain just scored!"
The honest cost analysis
For a fantasy World Cup app with up to 10,000 active users polling live:
- API costs: $50-129/month (Starter or Growth on TheStatsAPI)
- Supabase: free tier covers most of this; Pro $25/month if you exceed
- Vercel: free Hobby is fine until you scale
- Total: $50-150/month for the tournament
Plenty of room for a side-project, sponsored leagues, or affiliate deals to cover costs.
Frequently Asked Questions
What API data do I need for a fantasy World Cup app?
Squads, lineups (starting XI), player stats (goals, assists, minutes, cards), team stats (clean sheets), and match status (finished / in progress). TheStatsAPI returns all of these from a few endpoints.
How do I handle the World Cup's shorter format?
The 2026 World Cup spans 39 days with 3 group matches per team plus knockouts. Tune your fantasy gameweek structure to match-days (multiple matches per day) rather than weekly cycles like the Premier League.
How fast do player stats update during matches?
Within seconds of an event in our source feed. For live fantasy scoring, poll every 30-60 seconds during matches; refresh on goal/card webhook events for lower latency if your provider supports them.
Can I run this without a backend?
Mostly. Use localStorage for the user's squad, then call a serverless function to compute scores after each match. Or use Supabase as a cheap managed backend.
What's the cheapest API for this?
For prototyping, football-data.org free tier. For a real product with live scoring, TheStatsAPI's Starter plan at $50/month is the cleanest because it includes lineups, xG, and player stats — many cheaper APIs don't.
Do I need a license to run a fantasy game?
For free-to-play games with no prizes, no. If you're running a paid league or awarding prizes above a certain value, check the gambling laws in your jurisdiction — they vary significantly. Free brackets and leaderboards are safe everywhere.
How long does it take to build?
Roughly one focused weekend for an MVP. Squad picker on day 1, scoring + leaderboard on day 2. Polish, push notifications, and group leagues over the following week.
Ready to Power Your Sports App?
Start your 7-day free trial. All endpoints included on every plan.