Celebratory React dialog for newly unlocked achievements with headline, badge, and share actions. Use for unlock moments, level up animations and more. Built on shadcn/ui + Tailwind. Open source.
Achievement Unlocked is a focused gamification moment for celebrating unlocks, level up animations, congratulating users, reinforcing rewards, and encouraging sharing in achievement-driven gamification experiences.
Installation
npx shadcn@latest add https://ui.trophy.so/achievement-unlockedUsage
import { AchievementUnlocked } from "@/components/ui/achievement-unlocked"const [open, setOpen] = useState(false)
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-lg border bg-background px-4 py-2 text-sm font-medium text-foreground transition-colors hover:bg-muted"
>
Unlock achievement
</button>
<AchievementUnlocked
achievement={{
id: "task-champion",
name: "Task Champion",
description: "Complete a task 30 days in a row. Congratulations!!",
unlockedAt: new Date().toISOString(),
}}
open={open}
onOpenChange={setOpen}
secondaryActionLabel="Share"
onSecondaryActionClick={() => {
navigator.share?.({
title: "I unlocked Task Champion!",
text: "Complete a task 30 days in a row. Congratulations!!",
url: window.location.href,
})
}}
/>Examples
Basic Usage
const [open, setOpen] = useState(false)
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-lg border bg-background px-4 py-2 text-sm font-medium text-foreground transition-colors hover:bg-muted"
>
Unlock achievement
</button>
<AchievementUnlocked
achievement={{
id: "task-champion",
name: "Task Champion",
description: "Complete a task 30 days in a row. Congratulations!!",
unlockedAt: new Date().toISOString(),
}}
open={open}
onOpenChange={setOpen}
/>With Trophy
Use the Trophy SDK to send a metric event server-side:
import { TrophyApiClient } from '@trophyso/node';
const trophy = new TrophyApiClient({
apiKey: 'YOUR_API_KEY'
});
const response = await trophy.metrics.event(
"words-written",
{
user: {
id: 'user-id',
email: '[email protected]',
tz: 'Europe/London',
subscribedToEmails: true,
attributes: {
department: 'engineering',
role: 'developer'
}
},
value: 750,
attributes: {
category: 'writing',
source: 'mobile-app'
}
}
);
Then pass and transform the response into AchievementUnlocked props in your UI layer:
const unlockedAchievement = response.achievements?.[0]
const shouldOpen = Boolean(unlockedAchievement)
{
unlockedAchievement ? (
<AchievementUnlocked achievement={unlockedAchievement} open={shouldOpen} />
) : null
}API Reference
AchievementUnlocked
| Prop | Type | Default | Description |
|---|---|---|---|
achievement | Achievement | Required | The achievement to display |
open | boolean | Required | Controls dialog visibility |
onOpenChange | (open: boolean) => void | Required | Called when open state changes |
secondaryActionLabel | string | "Share" | Label for the secondary action button |
onSecondaryActionClick | () => void | - | Secondary button click handler for social share or custom actions |
onShare | () => void | - | Deprecated alias for onSecondaryActionClick |
className | string | - | Additional classes for the dialog |
Achievement
interface Achievement {
id: string
name: string
description?: string | null
unlockedAt?: string
}Power Gamification with Trophy
Trophy powers the infrastructure you need to ship reliable gamification features at scale.
Get Started