Points Boost
trophy/points-boostFreeComponent
React callout for points multipliers and limited-time boosts. Supports optional countdown for near-ending gamification campaigns. 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/points-boost.jsonSource
1"use client"23import * as React from "react"4import { ChevronRight } from "lucide-react"56import { cn } from "@/lib/utils"7import { Button } from "@/components/ui/button"89export type PointsBoostStatus = "active" | "scheduled" | "finished"1011interface PointsBoostCta {12 link: string13 text: string14}1516interface PointsBoostData {17 name: string18 status: PointsBoostStatus19 /** Optional subtitle below the title row. */20 description?: string21 multiplier: number22 endDate?: string | Date23}2425interface PointsBoostProps extends React.HTMLAttributes<HTMLDivElement> {26 boost: PointsBoostData27 cta: PointsBoostCta28}2930function formatCountdown(ms: number) {31 const totalSeconds = Math.max(0, Math.floor(ms / 1000))32 const hours = Math.floor(totalSeconds / 3600)33 const minutes = Math.floor((totalSeconds % 3600) / 60)34 const seconds = totalSeconds % 6035 return `Ends in ${hours}h ${minutes}m ${seconds}s`36}3738const PointsBoost = React.forwardRef<HTMLDivElement, PointsBoostProps>(39 ({ className, boost, cta, ...props }, ref) => {40 const endTimestamp = React.useMemo(() => {41 if (!boost.endDate) return null42 const date =43 boost.endDate instanceof Date ? boost.endDate : new Date(boost.endDate)44 const timestamp = date.getTime()45 return Number.isNaN(timestamp) ? null : timestamp46 }, [boost.endDate])4748 const [now, setNow] = React.useState(Date.now())4950 React.useEffect(() => {51 if (!endTimestamp) return52 const timer = window.setInterval(() => setNow(Date.now()), 1000)53 return () => window.clearInterval(timer)54 }, [endTimestamp])5556 const countdownLabel = React.useMemo(() => {57 if (!endTimestamp) return null58 const remaining = endTimestamp - now59 if (remaining <= 0) return "Ended"60 return formatCountdown(remaining)61 }, [endTimestamp, now])6263 return (64 <div65 ref={ref}66 className={cn(67 "bg-card text-foreground flex flex-col items-start justify-between gap-4 rounded-xl border px-4 py-3 lg:flex-row lg:items-center",68 className69 )}70 {...props}71 >72 <div className="flex min-w-0 flex-1 flex-col gap-0.5">73 <p className="flex flex-wrap items-center gap-2 font-semibold">74 <span className="min-w-0 truncate">{boost.name}</span>75 <span className="bg-muted-foreground/10 text-primary shrink-0 rounded-full px-2 py-0.5">76 x{boost.multiplier}77 </span>78 </p>79// … truncated
What it pulls in
npm packages
Other registry items
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 |
