Analytics Cards

Minimal metric cards with change indicators for dashboard analytics.

Preview

Features

  • Minimal, clean card layout
  • Three trend states: up (green), down (red), neutral (muted)
  • Inline percentage change
  • Clickable cards with href support
  • Loading skeleton state
  • Responsive grid container (2, 3, or 4 columns)

Install Summary

This kit will add the following files and dependencies to your project. Download the bundle and extract it into your project root.

Component Files

FilePath
analytics-card-grid.tsxcomponents/data/analytics-cards/analytics-card-grid.tsx
analytics-card.tsxcomponents/data/analytics-cards/analytics-card.tsx
types.tscomponents/data/analytics-cards/types.ts
index.tscomponents/data/analytics-cards/index.ts

Utility Files

FilePath
format.tslib/format.ts
design-tokens.tslib/design-tokens.ts

Installation

Download the complete bundle as a ZIP file, or copy the text bundle to your clipboard:

Component Files

The component consists of the following files:

1. analytics-card-grid.tsx

analytics-card-grid.tsx
tsx
import { cn } from "@/lib/utils";
import type { AnalyticsCardGridProps } from "./types";

const COLUMN_CLASSES = {
  2: "grid-cols-1 sm:grid-cols-2",

2. analytics-card.tsx

analytics-card.tsx
tsx
"use client";

import { Card, CardContent } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { TrendUp, TrendDown } from "@phosphor-icons/react";

3. types.ts

types.ts
tsx
import type { ReactNode } from "react";

export type TrendDirection = "up" | "down" | "neutral";

export interface AnalyticsCardProps {

4. index.ts

index.ts
tsx
export { AnalyticsCard } from "./analytics-card";
export { AnalyticsCardGrid } from "./analytics-card-grid";
export type {
  AnalyticsCardProps,
  AnalyticsCardGridProps,
  TrendDirection,
} from "./types";

Shared Utilities

This component uses shared utility functions. These are included in the bundle above:

format.ts

format.ts
tsx
export function formatDate(date: Date): string {
  return date.toLocaleDateString("en-US", {
    month: "short",
    day: "numeric",
    year: "numeric",
  });
}

export function formatRelativeTime(date: Date, now: Date = new Date()): string {
  const diff = now.getTime() - date.getTime();
  const minutes = Math.floor(diff / 60000);

  if (minutes < 1) return "Just now";
  if (minutes < 60) return `${minutes}m ago`;

  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;

  const days = Math.floor(hours / 24);
  if (days < 7) return `${days}d ago`;

  return formatDate(date);
}

export function formatPrice(amount: number, currency = "USD"): string {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency,
    minimumFractionDigits: 0,
    maximumFractionDigits: 2,
  }).format(amount);
}

export function formatNumber(num: number): string {
  if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;
  if (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`;
  return num.toString();
}

design-tokens.ts

design-tokens.ts
tsx
// Border radius
export const radius = {
  card: "rounded-xl",
  badge: "rounded-full",
  button: "rounded-lg",

Usage

app/dashboard.tsx
tsx
import { AnalyticsCard, AnalyticsCardGrid } from "@/components/data/analytics-cards";

function Dashboard() {
  return (
    <AnalyticsCardGrid columns={4}>

AnalyticsCard Props

PropTypeDefault
titlestringrequired
valuestring | numberrequired
changenumber-
trend"up" | "down" | "neutral""neutral"
hrefstring-
loadingbooleanfalse

AnalyticsCardGrid Props

PropTypeDefault
childrenReactNoderequired
columns2 | 3 | 44