GitHub

Achievement Grid

Responsive React achievement grid for displaying collections of badges with progress and rarity across breakpoints. Ideal achievement UI design for galleries and showcases in consumer apps. Open source, built on shadcn/ui + Tailwind.

Achievement Grid lays out badge collections in a scannable gamification surface—balanced columns, spacing rhythm, and built-in rarity or completion metadata for trophy rooms and showcase-style achievement gamification.

Early Bird
First Steps
Task Master

Installation

npx shadcn@latest add https://ui.trophy.so/achievement-grid

Usage

import { AchievementGrid } from "@/components/ui/achievement-grid"
<AchievementGrid
  achievements={[
    {
      id: "1",
      name: "Early Bird",
      trigger: "api",
      achievedAt: "2024-01-01T00:00:00Z",
    },
    {
      id: "2",
      name: "First Steps",
      trigger: "streak",
      achievedAt: "2024-01-03T00:00:00Z",
    },
    {
      id: "3",
      name: "Task Master",
      trigger: "metric",
      achievedAt: "2024-01-02T00:00:00Z",
    },
  ]}
/>

Examples

Basic Usage

Early Bird
First Steps
Task Master
<AchievementGrid
  achievements={[
    {
      id: "1",
      name: "Early Bird",
      trigger: "api",
      achievedAt: "2024-01-01T00:00:00Z",
    },
    {
      id: "2",
      name: "First Steps",
      trigger: "streak",
      achievedAt: "2024-01-03T00:00:00Z",
    },
    {
      id: "3",
      name: "Task Master",
      trigger: "metric",
      achievedAt: "2024-01-02T00:00:00Z",
    },
  ]}
/>

Column Layouts

Early Bird
First Steps
Task Master
Marathon
API Explorer
Early Bird
First Steps
Task Master
Marathon
API Explorer
Early Bird
First Steps
Task Master
Marathon
API Explorer
// Fixed columns
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  columns={2}
/>
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  columns={3}
/>
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  columns={4}
/>

// Auto-responsive (default)
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  columns="auto"
/>
// 2 cols mobile → 3 cols tablet → 4 cols desktop → 5 cols large

Gap Sizes

Early Bird
First Steps
Task Master
Marathon
API Explorer
Early Bird
First Steps
Task Master
Marathon
API Explorer
Early Bird
First Steps
Task Master
Marathon
API Explorer
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  gap="sm"
/>
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  gap="default"
/>
<AchievementGrid
  achievements={[
    { id: "1", name: "Early Bird", trigger: "api", achievedAt: "2024-01-01T00:00:00Z" },
    { id: "2", name: "First Steps", trigger: "streak", achievedAt: "2024-01-03T00:00:00Z" },
    { id: "3", name: "Task Master", trigger: "metric", achievedAt: "2024-01-02T00:00:00Z" },
  ]}
  gap="lg"
/>

AchievementGrid composes the AchievementBadge component internally. For visual badge options such as size, progress, rarity, and click handling, see Achievement Badge. Locked badges are determined by achievedAt === null on each item.

Complete Example

Consistency I
3% of usersDaily Legend
12% of usersPower User
Consistency II
<AchievementGrid
  achievements={[
    {
      id: "complete-1",
      name: "Consistency I",
      trigger: "streak",
      achievedAt: "2024-01-01T00:00:00Z",
      progress: 28,
    },
    {
      id: "complete-2",
      name: "Daily Legend",
      trigger: "streak",
      achievedAt: "2024-01-01T00:00:00Z",
      rarity: 3,
    },
    {
      id: "complete-3",
      name: "Power User",
      trigger: "metric",
      achievedAt: "2024-01-01T00:00:00Z",
      rarity: 12,
    },
    {
      id: "complete-4",
      name: "Consistency II",
      trigger: "streak",
      achievedAt: "2024-01-01T00:00:00Z",
      progress: 62,
    },
  ]}
  columns={4}
  onAchievementClick={handleClick}
/>

With Trophy

Use the Trophy SDK to fetch a user's achievements server-side:

import { TrophyApiClient } from '@trophyso/node';

const trophy = new TrophyApiClient({
  apiKey: 'YOUR_API_KEY'
});

const response = await trophy.users.achievements("user-id");

Then pass the response into AchievementGrid props in your UI layer:

<AchievementGrid achievements={response} />

API Reference

AchievementGrid

PropTypeDefaultDescription
achievementsUserAchievement[]RequiredArray of achievements to display
columns2 | 3 | 4 | "auto""auto"Number of grid columns
gap"sm" | "default" | "lg""default"Gap between grid items
badgeSize"sm" | "default" | "lg""default"Size of achievement badges
onAchievementClick(achievement) => void-Click handler for achievements

Types

interface Achievement {
  id: string
  name: string
  trigger: "metric" | "api" | "streak"
  badgeUrl?: string | null
  progress?: number // optional series progress from 0 to 100, unlocked only
  rarity?: number // optional rarity from 1 to 100 shown as "X% of users"
}

interface UserAchievement extends Achievement {
  achievedAt: string | null // null = locked
}