Streak Badge
trophy/streak-badgeFreeComponent
React streak counter UI in a compact badge supporting daily, weekly, monthly or yearly streaks. Built on shadcn/ui + Tailwind. Open source.
Live preview
live · rendered by the registry
Rendered by trophy on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.trophy.so/r/streak-badge.jsonSource
1"use client"23import * as React from "react"4import { cva, type VariantProps } from "class-variance-authority"5import { Flame } from "lucide-react"67import { cn } from "@/lib/utils"89// Types (inlined - only fields used by this component)10interface StreakResponse {11 length: number12 frequency: "daily" | "weekly" | "monthly"13}1415// Variants16const streakBadgeVariants = cva(17 "inline-flex flex-col items-center justify-center rounded-3xl border border-border/60 bg-card text-center text-card-foreground transition-colors",18 {19 variants: {20 size: {21 sm: "w-28 gap-1.5 p-3",22 default: "w-40 gap-2.5 p-5",23 lg: "w-52 gap-3 p-6",24 },25 },26 defaultVariants: {27 size: "default",28 },29 }30)3132// Props33interface StreakBadgeProps34 extends35 React.HTMLAttributes<HTMLDivElement>,36 VariantProps<typeof streakBadgeVariants> {37 /** Streak length value */38 length?: number39 /** Streak frequency used for label rendering */40 frequency?: StreakResponse["frequency"]41 /** Optional subtitle shown below streak length */42 subtitle?: string43 /** Custom icon to replace flame */44 icon?: React.ReactNode45}4647const StreakBadge = React.forwardRef<HTMLDivElement, StreakBadgeProps>(48 (49 { className, size, length, frequency = "daily", subtitle, icon, ...props },50 ref51 ) => {52 const streakLength = length ?? 05354 const frequencyLabel = {55 daily: "day",56 weekly: "week",57 monthly: "month",58 }[frequency]5960 const pluralLabel =61 streakLength === 1 ? frequencyLabel : `${frequencyLabel}s`6263 const iconSize = {64 sm: "h-10 w-10",65 default: "h-16 w-16",66 lg: "h-20 w-20",67 }[size ?? "default"]6869 const valueSize = {70 sm: "text-2xl",71 default: "text-5xl",72 lg: "text-6xl",73 }[size ?? "default"]7475 const subtitleSize = {76 sm: "text-xs",77 default: "text-sm",78 lg: "text-base",79 }[size ?? "default"]8081 const subtitleText = subtitle ?? "streak"82 const valueUnit = pluralLabel8384 // Build accessible label85 const ariaLabel = `${streakLength} ${pluralLabel} streak`8687 return (88 <div89 ref={ref}90 role="status"91 aria-label={ariaLabel}92 className={cn(streakBadgeVariants({ size }), className)}93 {...props}94 >95 {icon ?? (96 <Flame97 className={cn(iconSize, "text-primary shrink-0")}98 aria-hidden="true"99 />100 )}101 <span102// … truncated
What it pulls in
npm packages
Files it writes
More from trophy
All 18 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Achievement Badgeachievement-badge | trophy | Components | Free | 1 dep | |
| Achievement Cardachievement-card | trophy | Components | Free | no deps | |
| Achievement Gridachievement-grid | trophy | Components | Free | 2 deps | |
| Achievement Listachievement-list | trophy | Components | Free | 2 deps | |
| Achievement Unlockedachievement-unlocked | trophy | Components | Free | 1 dep | |
| Leaderboard Cardleaderboard-card | trophy | Components | Free | no deps | |
| Leaderboard Podiumleaderboard-podium | trophy | Components | Free | 2 deps | |
| Leaderboard Rankingsleaderboard-rankings | trophy | Components | Free | 2 deps |
