Points Chart
trophy/points-chartFreeComponent
React chart for points over time. Displays trends, campaigns, and seasonality in gamification analytics views. Complements badges and levels. Built on Recharts + 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-chart.jsonSource
1"use client"23import * as React from "react"4import { Star } from "lucide-react"5import {6 CartesianGrid,7 Line,8 LineChart,9 ReferenceLine,10 ResponsiveContainer,11 Tooltip,12 XAxis,13 YAxis,14} from "recharts"1516import { cn } from "@/lib/utils"1718interface PointsChartDataPoint {19 date: string20 total: number21 change: number22}2324interface PointsChartLevel {25 value: number26 color: string27}2829interface PointsChartProps extends React.HTMLAttributes<HTMLDivElement> {30 data: PointsChartDataPoint[]31 height?: number32 title?: string33 headerRight?: React.ReactNode34 yAxisLabel?: string35 levels?: PointsChartLevel[]36}3738function formatValue(value: number) {39 return Math.round(value).toLocaleString()40}4142function LevelReferenceStarLabel({43 viewBox,44 color,45}: {46 viewBox?: { x?: number; y?: number } | null47 color: string48}) {49 const x = viewBox?.x50 const y = viewBox?.y5152 if (typeof x !== "number" || typeof y !== "number") {53 return null54 }5556 return (57 <g transform={`translate(${x - 14},${y})`}>58 <Star59 x={-5}60 y={-5}61 width={10}62 height={10}63 fill={color}64 stroke={color}65 strokeWidth={1.75}66 />67 </g>68 )69}7071function PointsChart({72 data,73 height = 260,74 title = "Your points",75 headerRight,76 yAxisLabel,77 levels,78 className,79 ...props80}: PointsChartProps) {81 const yDomain = React.useMemo<[number, number]>(() => {82 const values = [83 ...data.map((item) => item.total),84 ...(levels?.map((level) => level.value) ?? []),85 ]8687 if (values.length === 0) return [0, 100]8889 const minValue = Math.min(...values)90 const maxValue = Math.max(...values)91 const range = maxValue - minValue9293 if (range === 0) {94 const padding = Math.max(maxValue * 0.15, 10)95 return [Math.max(0, minValue - padding), maxValue + padding]96 }9798 const padding = Math.max(range * 0.12, 10)99 return [Math.max(0, minValue - padding), maxValue + padding]100 }, [data, levels])101102 return (103 <div className={cn("bg-card rounded-2xl border p-4", className)} {...props}>104 <div className="mb-3 flex items-center justify-between gap-3">105 <p className="text-md text-foreground font-semibold">{title}</p>106 {headerRight ? <div className="shrink-0">{headerRight}</div> : null}107 </div>108 <div style={{ height }}>109 <ResponsiveContainer width="100%" height="100%">110 <LineChart111 data={data}112// … 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 |
