New

StatsComparison

Side-by-side metric comparison with visual progress bars and delta indicators

Dashboard & Analyticscomparisonstatsmetricsdelta

Dependencies

shadcn/ui components needed:

npx shadcn@latest add cardnpx shadcn@latest add badgenpx shadcn@latest add separator

How to use this component

Copy the code below into your project. Make sure you have the required shadcn/ui dependencies installed. Then import and use the component in your pages or layouts.

Code

1"use client"
2
3import * as React from "react"
4import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
5import { Badge } from "@/components/ui/badge"
6import { Separator } from "@/components/ui/separator"
7import { cn } from "@/lib/utils"
8
9interface Metric {
10 name: string
11 thisPeriod: number
12 lastPeriod: number
13}
14
15interface StatsComparisonProps {
16 metrics?: Metric[]
17 className?: string
18}
19
20export function StatsComparison({ metrics = [
21 { name: "Revenue", thisPeriod: 84500, lastPeriod: 72000 },
22 { name: "Users", thisPeriod: 12400, lastPeriod: 10800 },
23 { name: "Orders", thisPeriod: 3200, lastPeriod: 3500 },
24 { name: "Conversion", thisPeriod: 3.2, lastPeriod: 2.8 },
25], className }: StatsComparisonProps) {
26 const formatValue = (val: number) => val >= 1000 ? `${(val / 1000).toFixed(1)}k` : val.toFixed(1)
27
28 return (
29 <Card className={cn("w-full", className)}>
30 <CardHeader>
31 <CardTitle>Period Comparison</CardTitle>
32 </CardHeader>
33 <CardContent>
34 <div className="space-y-4">
35 {metrics.map((metric, i) => {
36 const delta = ((metric.thisPeriod - metric.lastPeriod) / metric.lastPeriod) * 100
37 const isPositive = delta >= 0
38 const maxValue = Math.max(metric.thisPeriod, metric.lastPeriod)
39
40 return (
41 <div key={i}>
42 <div className="flex items-center justify-between mb-2">
43 <span className="font-medium text-sm">{metric.name}</span>
44 <Badge variant={isPositive ? "default" : "destructive"} className={cn(
45 "text-xs",
46 isPositive ? "bg-emerald-500/10 text-emerald-600 hover:bg-emerald-500/20" : "bg-red-500/10 text-red-600 hover:bg-red-500/20"
47 )}>
48 {isPositive ? "+" : ""}{delta.toFixed(1)}%
49 </Badge>
50 </div>
51 <div className="grid grid-cols-2 gap-4 mb-2">
52 <div>
53 <p className="text-xs text-muted-foreground">This Period</p>
54 <p className="text-lg font-semibold">{formatValue(metric.thisPeriod)}</p>
55 </div>
56 <div className="text-right">
57 <p className="text-xs text-muted-foreground">Last Period</p>
58 <p className="text-lg font-semibold text-muted-foreground">{formatValue(metric.lastPeriod)}</p>
59 </div>
60 </div>
61 <div className="flex gap-1 h-2 rounded-full overflow-hidden bg-muted">
62 <div
63 className={cn("rounded-l-full transition-all duration-500", isPositive ? "bg-blue-500" : "bg-blue-400")}
64 style={{ width: `${(metric.thisPeriod / maxValue) * 50}%` }}
65 />
66 <div
67 className={cn("rounded-r-full transition-all duration-500", "bg-slate-400")}
68 style={{ width: `${(metric.lastPeriod / maxValue) * 50}%` }}
69 />
70 </div>
71 {i < metrics.length - 1 && <Separator className="mt-4" />}
72 </div>
73 )
74 })}
75 </div>
76 </CardContent>
77 </Card>
78 )
79}

Related Dashboard & Analytics Components

Command Palette

Search for a command to run...