Esports · Implementation reference

The Esports
match blueprint

Live scoreboards, post-match recaps, standings tables, and player telemetry — every esports surface derives from the Match, Team, and Player models documented here.

Models

3

Statuses

live · upcoming · final

Card variants

3

Routes

/esports/$matchId

01Scoreboard

Live match hero

The detail page leads with the live scoreboard, format/map context, and tournament metadata aside.

LIVEValor Arena Champions · Upper Final512,000 watching
SEN

Sentinel

NA

2:1

BO5 · Map 4 — Haven (8:6)

IRM

Iron Maple

EU

Objective control

SEN 56% · IRM 44%

First kills

SEN 18 · IRM 14

Avg round time

1m 29s

02Series detail

Map summary & player table

Per-map outcomes plus compact player telemetry; both share team FKs with the scoreboard.

Map / series summary
M1Ascent
13-8Sentinel
M2Bind
10-13Iron Maple
M3Lotus
13-11Sentinel
Player stats
PlayerTeamRoleRatingKDASignature
PhantomSENDuelist1.341.62Jett
AegisSENInitiator1.221.41Sova
AriaIRMController1.211.39Omen
IrisIRMSentinel1.121.18Killjoy
04Discovery

MatchCard variants

The same MatchCard renders the three states with subtle status accents.

CS2 · CS2 Paris Major · Semifinalsoon
VXR
Vexar
EU
0:0
BO3
NST
North Star
NA
League of Legends · LEC Spring Playoffs · Grand FinalFINAL
ATL
Atlas Esports
EU
1:3
BO5
K9
Kingdom Nine
KR
Dota 2 · ESL One Finals · Winner Bracketsoon
SPH
Spirit Hunters
CIS
0:0
BO3
DYN
Dynasty
CN
05Schema

Match model & sample payload

src/data/esports.ts
typescript
export interface Match {
  id: string;
  game: EsportsGame;        // Valorant | CS2 | LoL | Dota 2
  tournament: string;
  status: "live" | "upcoming" | "finished";
  startsAt: string;         // ISO 8601
  teamAId: string;          // FK -> Team.id
  teamBId: string;
  scoreA: number;
  scoreB: number;
  format: string;           // "BO3", "BO5"
  currentMap?: string;      // live ticker
  viewers?: number;         // live concurrency
}
GET /api/matches/:id
json
{
  "id": "vct-2026-uf-sen-irm",
  "game": "Valorant",
  "tournament": "Valor Arena Champions · Upper Final",
  "status": "live",
  "startsAt": "2026-05-07T18:30:00.000Z",
  "teamAId": "sentinel",
  "teamBId": "iron-maple",
  "scoreA": 2,
  "scoreB": 1,
  "format": "BO5",
  "currentMap": "Map 4 — Haven (8:6)",
  "viewers": 512000
}

Match fields

idrequired
string

Opaque match id; powers /esports/$matchId.

gamerequired
EsportsGame

Enum: Valorant | CS2 | League of Legends | Dota 2.

tournamentrequired
string

Series + stage label, e.g. 'Champions · Upper Final'.

statusrequired
'live' | 'upcoming' | 'finished'

Drives badge color and card variant.

startsAtrequired
string (ISO)

ISO 8601; powers countdown + ordering.

teamAIdrequired
string

FK → Team.id (left side).

teamBIdrequired
string

FK → Team.id (right side).

scoreArequired
number

Maps won or game points.

scoreBrequired
number

Same as scoreA.

formatrequired
string

Short label, 'BO3' / 'BO5'.

currentMap
string?

Live ticker string; live status only.

viewers
number?

Live concurrency count.

06Backend

Cross-model alignment

Match references Team and Player by id — keep these consistent across endpoints.

Team contract

  • tag — 2–3 char monogram, drives crests.
  • logoColor — hex for crest fill.
  • formStreak — last 5 results, ('W'|'L')[].
  • wins · losses · points — standings columns.
  • region · game — partition for filters.

Player contract

  • teamId — FK → Team.id; required.
  • game — must match team.game.
  • role — game-specific string.
  • rating · kda — numeric, decimal places preserved.
  • signature — agent / champion / hero name.

Tip

When status === "live", also return currentMap and viewers — the UI hides the badge if either is missing.