Skip to content
Neon UI
Esc
navigateopen⌘Jpreview
On this page

MetricCard

Analytics card with a formatted value, signed delta, and interactive Visx sparkline.

MetricCard is the reusable analytics card the rest of the registry builds on: UsageCard, CostMeter, and HealthCard all compose from it. It takes a value, an optional signed delta, and an optional labeled trend series that renders as an interactive sparkline. Loading and error states are props, so it drops into any data layer.

Active connections
+12%vs yesterday
42

Variants

Data formats

Number, byte, percentage, duration, and currency formatting use the same card anatomy.

Storage used
+4%vs previous 10 days
3.4 GB
Compute this month
8%vs previous 10 days
128hrs
Cache hit ratio
0%vs yesterday
99.2%
Estimated spend
+19%vs last month
$284.50

Delta states

Comparison context makes positive, negative, and unchanged deltas meaningful.

Active connections
+12%vs yesterday
42
Compute this month
8%vs previous 10 days
128hrs
Cache hit ratio
0%vs yesterday
99.2%

System states

Loading, error, and no-trend states preserve the card’s layout without inventing chart data.

Active connections
Loading metric data
Projects
+3%vs yesterday
12
No trend data

Install

npx shadcn@latest add https://ui.neon.com/r/metric-card.json

Usage

import { MetricCard } from "@/components/neon-ui/metric-card";

<MetricCard
  format="number"
  label="Active connections"
  value={42}
  delta={12}
  trend={[18, 22, 27, 31, 34, 38, 42]}
/>;

Wiring to Neon

The presentational component takes data as props. A server component fetches real consumption from the Neon API and passes it down:

import { createNeonClient } from "@/lib/neon-client";

const neon = createNeonClient(process.env.NEON_API_KEY);
const { data } = await neon.consumption
  .perProject({
    from: from.toISOString(),
    to: now.toISOString(),
    granularity: "daily",
    project_ids: [projectId],
  })
  .all();

Props

PropType
labelstring

Metric name shown above the value.

Typestring
valuenumber | string

The metric value. Strings render verbatim; numbers are formatted.

Typenumber | string
delta?number

Signed change vs the previous period. Positive is green, negative is red.

Typenumber
comparisonLabel?string

Context shown with the delta, such as "vs yesterday".

Typestring
trend?(number | MetricTrendPoint)[]

Series driving the sparkline. Labeled points produce useful tooltips; an empty array renders the no-trend state.

Type(number | MetricTrendPoint)[]
format?"number" | "bytes" | "percent" | "currency" | "duration"

How a numeric value is formatted.

Type"number" | "bytes" | "percent" | "currency" | "duration"
Default"number"
unit?ReactNode

Suffix rendered after the value, e.g. "hrs".

TypeReactNode
isLoading?boolean

Render a skeleton placeholder.

Typeboolean
Defaultfalse
error?Error | string | null

Render an error state with the message.

TypeError | string | null
Defaultnull

Formatting

format controls how a numeric value is displayed:

  • number — grouped integer (1,204)
  • bytes — human-readable size (3.6 GB)
  • percent — value with a % suffix (99.2%)
  • currency — USD ($284.50)
  • duration — value in seconds as h/m/s (2h 8m)

Accessibility

The sparkline is keyboard accessible. Focus it and use Left/Right, Home, or End to inspect points. The active label and formatted value are announced through a polite live region, and the signed delta uses both an icon and text so meaning never relies on color alone.