← Optimotive UI/components/Metric Card
component

Metric Card

Stat card with icon, large value, label, and trend indicator (up/down %) for dashboards.

Installation

npx shadcn@latest add https://optimotive-ui.dev.optimotive-tools.co.uk/registry.json metric-card

Preview

Source

components/metric-card.tsx
"use client"

import * as React from "react"
import { TrendingUp, TrendingDown, Minus } from "lucide-react"
import { cn } from "@/lib/utils"

export interface MetricCardProps extends React.HTMLAttributes<HTMLDivElement> {
  label: string
  value: string | number
  trend?: number
  icon?: React.ReactNode
  description?: string
}

export function MetricCard({ label, value, trend, icon, description, className, ...props }: MetricCardProps) {
  const isUp = trend !== undefined && trend > 0
  const isDown = trend !== undefined && trend < 0
  const trendColor = isUp ? "text-emerald-400" : isDown ? "text-red-400" : "text-slate-400"
  const TrendIcon = isUp ? TrendingUp : isDown ? TrendingDown : Minus

  return (
    <div
      className={cn(
        "rounded-xl border border-slate-800 bg-slate-900/60 p-6 transition-colors hover:border-slate-700",
        className
      )}
      {...props}
    >
      <div className="flex items-start justify-between mb-3">
        <p className="text-sm font-medium text-slate-400">{label}</p>
        {icon && <div className="text-slate-500">{icon}</div>}
      </div>
      <p className="text-3xl font-bold text-white tracking-tight mb-2">{value}</p>
      {trend !== undefined && (
        <div className={cn("flex items-center gap-1 text-sm font-medium", trendColor)}>
          <TrendIcon className="w-3.5 h-3.5" />
          <span>{Math.abs(trend)}%</span>
          {description && <span className="text-slate-500 font-normal ml-1">{description}</span>}
        </div>
      )}
      {trend === undefined && description && (
        <p className="text-sm text-slate-500">{description}</p>
      )}
    </div>
  )
}