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
| File | Path |
|---|---|
| analytics-card-grid.tsx | components/data/analytics-cards/analytics-card-grid.tsx |
| analytics-card.tsx | components/data/analytics-cards/analytics-card.tsx |
| types.ts | components/data/analytics-cards/types.ts |
| index.ts | components/data/analytics-cards/index.ts |
Utility Files
| File | Path |
|---|---|
| format.ts | lib/format.ts |
| design-tokens.ts | lib/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
| Prop | Type | Default |
|---|---|---|
| title | string | required |
| value | string | number | required |
| change | number | - |
| trend | "up" | "down" | "neutral" | "neutral" |
| href | string | - |
| loading | boolean | false |
AnalyticsCardGrid Props
| Prop | Type | Default |
|---|---|---|
| children | ReactNode | required |
| columns | 2 | 3 | 4 | 4 |