M
MetricUI
Guide

Data States

Every MetricUI component handles four data states out of the box: loading, empty, error, and stale. No conditional rendering needed.

Overview

Instead of wrapping components in loading spinners or error boundaries manually, pass the state directly to any component. MetricUI renders the appropriate skeleton, message, or indicator automatically.

Loading

Shows a skeleton placeholder that matches the component's layout. The skeleton uses CSS variables for colors — no hardcoded grays.

<KpiCard title="Revenue" value={0} format="currency" loading />

<AreaChart data={[]} title="Trends" loading />

<DataTable data={[]} columns={columns} loading />

Empty

Displayed when there's no data to show. Customizable message, icon, and action.

<KpiCard
  title="Revenue"
  value={0}
  empty={{
    message: "No revenue data for this period",
    icon: <BarChart className="h-8 w-8" />,
    action: { label: "Change period", onClick: () => {} },
  }}
/>

// Or just a simple message
<AreaChart data={[]} empty={{ message: "No data available" }} />

Error

Shows an error message with an optional retry button. In dev mode, includes component name and stack trace.

<KpiCard
  title="Revenue"
  value={0}
  error={{ message: "Failed to load data", onRetry: () => refetch() }}
/>

Stale

Shows a subtle indicator that the data is outdated. The component still renders its value — just with a stale badge.

<KpiCard
  title="Revenue"
  value={142300}
  format="currency"
  stale={{ message: "Updated 5 min ago" }}
/>

Grouped State Prop

Instead of individual props, use the state prop to pass all states at once. This is cleaner when states come from a data-fetching hook.

const { data, isLoading, error } = useSWR("/api/revenue");

<KpiCard
  title="Revenue"
  value={data?.value ?? 0}
  format="currency"
  state={{
    loading: isLoading,
    error: error ? { message: error.message } : undefined,
    empty: !data ? { message: "No data" } : undefined,
  }}
/>

The state prop takes precedence over individual loading/empty/error/stale props.

Global Defaults

Set default empty and error state templates via MetricProvider:

<MetricProvider
  emptyState={{ message: "No data available" }}
  errorState={{ message: "Something went wrong" }}
>
  {/* All components inherit these defaults */}
</MetricProvider>