Fixtures

Animated tournament fixtures in two styles: a knockout bracket that pages through rounds, and a wheel that wraps the same tree around the champion. Both read the same array of rounds, so one dataset draws either.

Knockout Wheel

knockout-wheel.tsx

The tournament drawn radially. The champion holds the hub, each round is a ring further out, and the teams themselves form the rim. Nodes spring in ring by ring, and hovering one isolates that team while the rest recede. Teams show a flag, a logo or their initials, and a deeper draw grows another ring.

SSASBSPSJSCRJSNPNEGPEBEUEWCUCBCDBCFAFIFMFPSMBIBTSIAGAMAPQMGNGSAN
  • Spain v Argentina · 2–1
  • Spain v Brazil · 2–1
  • France v Argentina · 0–0 (3–4 pens)
  • Spain v Portugal · 1–0
  • England v Brazil · 2–3
  • France v Italy · 2–1
  • Argentina v Germany · 3–1
  • Spain v Japan · 2–0
  • Netherlands v Portugal · 1–3
  • England v Uruguay · 2–1
  • Croatia v Brazil · 0–1
  • France v Morocco · 3–1
  • Belgium v Italy · 1–2
  • Argentina v Mexico · 2–0
  • Germany v Norway · 1–1 (4–2 pens)
  • Spain v Costa Rica · 3–0
  • Japan v Serbia · 2–1
  • Netherlands v Ecuador · 2–0
  • Ghana v Portugal · 2–3
  • England v Wales · 4–0
  • Canada v Uruguay · 0–2
  • Croatia v Denmark · 1–0
  • Brazil v Cameroon · 3–1
  • France v Poland · 2–1
  • Senegal v Morocco · 0–1
  • Belgium v Tunisia · 2–0
  • Switzerland v Italy · 1–3
  • Argentina v Peru · 2–0
  • Qatar v Mexico · 0–1
  • Germany v Sweden · 4–2
  • Austria v Norway · 1–2

Install

$ bunx --bun shadcn add @beui/knockout-wheel

API Reference

rounds
Round[]

The whole draw, ordered widest round first — the same array the knockout bracket takes. Any single-elimination tournament fits: each round holds half the matches of the one before it (16 → 8 → 4 → 2 → 1) and `rounds[r].matches[k]` is fed by matches `2k` and `2k + 1` of the round before it. Two rounds are enough; the wheel grows a ring per round and sizes itself to the rim.

—
initialRound?
number

Index of the outermost round to draw. Earlier rounds are dropped and the kept round's own teams become the rim, so `1` on a 32-team draw opens at the Round of 16. Defaults to 0 (the whole tree); clamped to the valid range.

0
className?
string
—

Knockout Bracket

knockout-bracket.tsx

Pages one round at a time. The leftmost round stacks at a fixed rhythm, each later round centers between its two feeder matches, and cards, elbow connectors, headers and stage height animate into every new layout. A third place play-off sits below the tree under its own rule. Round names, team artwork, dates and result chips all come from the data you pass.

Showing Round of 16, Quarter-finals, and Semi-finals
Round of 16
Quarter-finals
Semi-finals
  • Sat, 4 JulFT
    Canada0
    Morocco3
  • Sun, 5 JulFT
    Paraguay0
    France1
  • Mon, 6 JulFT
    USA1
    Belgium4
  • Mon, 6 JulFT
    Portugal0
    Spain1
  • Mon, 6 JulFT
    Brazil1
    Norway2
  • Mon, 6 JulFT
    Mexico2
    England3
  • Tue, 7 JulFT (P)
    Switzerland0 (4)
    Colombia0 (3)
  • Tue, 7 JulFT
    Argentina3
    Egypt2
  • Fri, 10 Jul, 4:00 amFT
    France2
    Morocco0
  • Sat, 11 Jul, 3:00 amFT
    Spain2
    Belgium1
  • TodayFT
    Norway1
    England2
  • TodayFT
    Argentina3
    Switzerland1
  • Wed, 15 Jul, 4:00 am
    France
    Spain
  • Thu, 16 Jul, 3:00 am
    England
    Argentina
  • Third place play-off

    Sun, 19 Jul, 3:00 am
    TBD
    TBD

Install

$ bunx --bun shadcn add @beui/knockout-bracket

API Reference

rounds
Round[]

The whole draw, ordered widest round first. Any single-elimination tournament fits: each round holds half the matches of the one before it (16 → 8 → 4 → 2 → 1) and `rounds[r].matches[k]` is fed by matches `2k` and `2k + 1` of the round before it. Two rounds are enough.

—
initialRound?
number

Round shown as the leftmost column on mount. Defaults to 1, clamped to the valid range.

1
thirdPlace?
Match

Third place play-off, rendered under the bracket instead of inside it.

—
thirdPlaceLabel?
string

Heading over `thirdPlace`. Defaults to "Third place play-off".

Third place play-off
className?
string
—

Keep in mind

Some components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.

Updated