-

Usage Stat Card

PreviousNext

A compact metric card for requests, token spend, latency, and quota usage.

Installation

pnpm
pnpm dlx shadcn@latest add https://ui.zgi.ai/r/usage-stat-card.json
npm
pnpm dlx shadcn@latest add https://ui.zgi.ai/r/usage-stat-card.json
yarn
yarn dlx shadcn@latest add https://ui.zgi.ai/r/usage-stat-card.json

Component

components/ui/usage-stat-card.tsx
"use client"

import * as React from "react"
import { ArrowDownIcon, ArrowRightIcon, ArrowUpIcon } from "lucide-react"

import { cn } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"

export type UsageStatTone = "neutral" | "good" | "warning" | "danger"
export type UsageStatTrend = "up" | "down" | "flat"

export interface UsageStatCardProps
  extends Omit<React.ComponentProps<typeof Card>, "title"> {
  label: React.ReactNode
  value: React.ReactNode
  description?: React.ReactNode
  icon?: React.ReactNode
  trend?: UsageStatTrend
  trendLabel?: React.ReactNode
  tone?: UsageStatTone
  footer?: React.ReactNode
}

const toneClassName: Record<UsageStatTone, string> = {
  neutral: "bg-muted text-muted-foreground",
  good: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
  warning: "bg-amber-500/10 text-amber-700 dark:text-amber-300",
  danger: "bg-destructive/10 text-destructive",
}

const trendIcon = {
  up: ArrowUpIcon,
  down: ArrowDownIcon,
  flat: ArrowRightIcon,
}

export function UsageStatCard({
  label,
  value,
  description,
  icon,
  trend,
  trendLabel,
  tone = "neutral",
  footer,
  className,
  ...props
}: UsageStatCardProps) {
  const TrendIcon = trend ? trendIcon[trend] : null

  return (
    <Card className={cn("rounded-lg py-0", className)} {...props}>
      <CardContent className="space-y-4 p-4">
        <div className="flex items-start justify-between gap-3">
          <div className="min-w-0">
            <p className="text-muted-foreground text-xs font-medium">{label}</p>
            <div className="mt-2 text-2xl font-semibold tracking-normal tabular-nums">
              {value}
            </div>
          </div>
          {icon ? (
            <div
              className={cn(
                "flex size-9 shrink-0 items-center justify-center rounded-lg",
                toneClassName[tone]
              )}
            >
              <span className="[&_svg]:size-4">{icon}</span>
            </div>
          ) : null}
        </div>

        <div className="flex min-h-6 flex-wrap items-center gap-2">
          {TrendIcon && trendLabel ? (
            <Badge
              variant="secondary"
              className={cn("h-6 rounded-md border-0", toneClassName[tone])}
            >
              <TrendIcon className="size-3.5" />
              {trendLabel}
            </Badge>
          ) : null}
          {description ? (
            <span className="text-muted-foreground text-xs leading-5">
              {description}
            </span>
          ) : null}
        </div>

        {footer ? (
          <div className="text-muted-foreground border-t pt-3 text-xs">
            {footer}
          </div>
        ) : null}
      </CardContent>
    </Card>
  )
}

Usage

import { ActivityIcon } from "lucide-react"
 
import { UsageStatCard } from "@/components/ui/usage-stat-card"
 
export function Example() {
  return (
    <UsageStatCard
      label="Requests"
      value="42.8k"
      trend="up"
      trendLabel="+12%"
      tone="good"
      icon={<ActivityIcon />}
    />
  )
}

Props

PropTypeDefault
labelReactNoderequired
valueReactNoderequired
descriptionReactNode-
iconReactNode-
trend"up" | "down" | "flat"-
trendLabelReactNode-
tone"neutral" | "good" | "warning" | "danger""neutral"
footerReactNode-