React leaderboard component that composes title, date range, podium, and rankings—one card for competitive gamification UIs. Fits Next.js and React apps. Built on shadcn/ui + Tailwind. Open source.
Leaderboard Card bundles competitive gamification UI for React—a Leaderboard Podium plus Leaderboard Rankings in one card for challenges, contests, and social gamification surfaces, with collapsible rows and easy highlighting for the current user.
Weekly Leaderboard
May 1, 2026 - May 7, 2026
Ava Elizabeth Turner
Level 42 - Diamond
289.4k
Leo Harrison
Level 39 - Platinum
251.8k
Rowan Elijah
Level 37 - Platinum
238.3k
Olivia Reed
Level 32 - Gold
210.9k
Ethan Brooks
Level 28 - Silver
187.2k
Chloe Madison
Level 27 - Silver
176.8k
Installation
npx shadcn@latest add https://ui.trophy.so/leaderboard-cardUsage
import { LeaderboardCard } from "@/components/ui/leaderboard-card"<LeaderboardCard
title="Weekly Leaderboard"
fromDate="2026-05-01"
toDate="2026-05-07"
currentUserId="u-5"
podiumRankings={[
{ userId: "u-1", userName: "Ava Elizabeth Turner", rank: 1, value: 289400 },
{ userId: "u-2", userName: "Leo Harrison", rank: 2, value: 251800 },
{ userId: "u-3", userName: "Rowan Elijah", rank: 3, value: 238300 },
]}
rankings={[
{
userId: "u-1",
rank: 1,
userName: "Ava Elizabeth Turner",
byline: "Level 42 - Diamond",
value: 289400,
displayed: true,
},
{
userId: "u-2",
rank: 2,
userName: "Leo Harrison",
byline: "Level 39 - Platinum",
value: 251800,
displayed: true,
},
{
userId: "u-3",
rank: 3,
userName: "Rowan Elijah",
byline: "Level 37 - Platinum",
value: 238300,
displayed: true,
},
{
userId: "u-4",
rank: 4,
userName: "Mia Sophia Bennett",
byline: "Level 34 - Gold",
value: 221700,
displayed: false,
},
{
userId: "u-5",
rank: 5,
userName: "Olivia Reed",
byline: "Level 32 - Gold",
value: 210900,
displayed: true,
},
{
userId: "u-6",
rank: 6,
userName: "William Turner",
byline: "Level 31 - Gold",
value: 199500,
displayed: false,
},
{
userId: "u-7",
rank: 7,
userName: "Ruby Claire",
byline: "Level 29 - Silver",
value: 198300,
displayed: false,
},
{
userId: "u-8",
rank: 8,
userName: "Ethan Brooks",
byline: "Level 28 - Silver",
value: 187200,
displayed: true,
},
{
userId: "u-9",
rank: 9,
userName: "Chloe Madison",
byline: "Level 27 - Silver",
value: 176800,
displayed: true,
},
{
userId: "u-10",
rank: 10,
userName: "Noah Bennett",
byline: "Level 26 - Silver",
value: 169300,
displayed: false,
},
]}
/>Examples
Basic Usage
Weekly Leaderboard
May 1, 2026 - May 7, 2026
Ava Elizabeth Turner
Level 42 - Diamond
289.4k
Leo Harrison
Level 39 - Platinum
251.8k
Rowan Elijah
Level 37 - Platinum
238.3k
Olivia Reed
Level 32 - Gold
210.9k
Ethan Brooks
Level 28 - Silver
187.2k
Chloe Madison
Level 27 - Silver
176.8k
<LeaderboardCard
title="Weekly Leaderboard"
fromDate="2026-05-01"
toDate="2026-05-07"
currentUserId="u-5"
podiumRankings={podiumRankings}
rankings={rankings}
/>With Previous Runs
Weekly Leaderboard
May 1, 2026 - May 7, 2026
Ava Elizabeth Turner
Level 42 - Diamond
289.4k
Leo Harrison
Level 39 - Platinum
251.8k
Rowan Elijah
Level 37 - Platinum
238.3k
Olivia Reed
Level 32 - Gold
210.9k
Ethan Brooks
Level 28 - Silver
187.2k
Chloe Madison
Level 27 - Silver
176.8k
const [selectedRunId, setSelectedRunId] = useState("this-week")
const runs = {
"this-week": { fromDate: "2026-05-01", toDate: "2026-05-07", podiumRankings, rankings },
"last-week": { fromDate: "2026-04-24", toDate: "2026-04-30", podiumRankings: lastWeekPodiumRankings, rankings: lastWeekRankings },
// ...older runs
}
const selectedRun = runs[selectedRunId] ?? runs["this-week"]
<LeaderboardCard
title="Weekly Leaderboard"
fromDate={selectedRun.fromDate}
toDate={selectedRun.toDate}
currentUserId="u-5"
selectedRunId={selectedRunId}
onRunChange={setSelectedRunId}
runOptions={[
{ id: "this-week", label: "This week" },
{ id: "last-week", label: "Last week" },
{ id: "2026-04-20", label: "Apr 17 - Apr 23" },
{ id: "2026-04-13", label: "Apr 10 - Apr 16" },
{ id: "2026-04-06", label: "Apr 3 - Apr 9" },
]}
podiumRankings={selectedRun.podiumRankings}
rankings={selectedRun.rankings}
/>With Trophy
Use the Trophy SDK to fetch leaderboard data server-side:
import { TrophyApiClient } from '@trophyso/node';
const trophy = new TrophyApiClient({
apiKey: 'YOUR_API_KEY'
});
const response = await trophy.leaderboards.get("weekly-words", {
offset: 0,
limit: 10,
run: "2025-01-15"
});
Then pass and transform the response into LeaderboardCard props in your UI layer:
const rankings = response.rankings ?? []
const podiumRankings = rankings.slice(0, 3)
<LeaderboardCard
title={response.name}
fromDate={response.start}
toDate={response.end}
podiumRankings={podiumRankings}
rankings={rankings}
/>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | "Leaderboard" | Card heading text |
fromDate | string | Date | Required | Start date of the leaderboard window |
toDate | string | Date | Required | End date of the leaderboard window |
podiumRankings | LeaderboardRanking[] | Required | Data for the top 3 podium section |
rankings | LeaderboardRankingItem[] | Required | Rankings list data |
currentUserId | string | - | Highlights the matching user row |
runOptions | { id: string; label: string }[] | - | Optional list of leaderboard runs shown in a selector |
selectedRunId | string | - | Currently selected run option id |
onRunChange | (runId: string) => void | - | Called when the run selector value changes |