Skip to content

Stat View

The stat view renders a responsive grid of KPI cards. Values are computed server-side and embedded in the spec — the UI only formats and displays them.

Preview
$48,290
Revenue
+12% vs last month
1,204
Active Users
active this month
3.1%
Conversion
5 GB
Storage Used
Spec
typescript
const spec = new StatView()
  .stat({ label: 'Revenue', value: 48290.50, format: 'currency' })
  .stat({ label: 'Active Users', value: 1204, description: 'active this month' })
  .stat({ label: 'Conversion', value: 0.0312, format: 'percent' })
  .stat({ label: 'Storage Used', value: 5368709120, format: 'bytes' })
  .title('Dashboard Overview')
  .build();

How it works

The SPA navigates to #/{resource}/stats and fetches a StatSpec from GET /api/ui/{resource}/stats. Each Stat object carries a pre-computed value — there is no client-side data fetching for individual metrics.

Basic setup (JS)

typescript
import { StatView } from '@retrofit-ui/builder-zod';

const spec = new StatView()
  .stat({ label: 'Revenue', value: 48290.50, format: 'currency' })
  .stat({ label: 'Active Users', value: 1204, description: 'active this month' })
  .stat({ label: 'Conversion', value: 0.0312, format: 'percent' })
  .stat({ label: 'Storage Used', value: 5368709120, format: 'bytes' })
  .title('Dashboard Overview')
  .build();

// URL prefix below is arbitrary — pick anything and match `apiBase` to it.
app.get('/dashboard/stats', (_req, res) => {
  res.json(spec);
});

Value formats

The format field controls how numeric values are displayed. String values are always rendered as-is.

formatInputOutput
'number' (default)12041,204
'currency'48290.50$48,290.50
'percent'0.03123.1%
'bytes'53687091205 GB

Currency defaults to USD. Override with the currency field:

typescript
.stat({ label: 'Revenue', value: 48290.50, format: 'currency', currency: 'EUR' })
// → €48,290.50

Locale formatting uses Intl.NumberFormat with the browser's locale — numbers, currency symbols, and decimal separators adapt automatically.

Description text

Each card accepts an optional description rendered below the label in muted text — useful for context like "vs last month" or units.

Description text
$9,800.50
Revenue
vs last month
42
Open Issues
↑ 3 since yesterday
Spec
typescript
new StatView()
  .stat({ label: 'Revenue', value: 9800.50, format: 'currency', description: 'vs last month' })
  .stat({ label: 'Open Issues', value: 42, description: '↑ 3 since yesterday' })
  .build();

Stat with trend

Include a trend indicator in the description string to give at-a-glance context alongside the main value.

Stat with trend
$48,290
▲ 12%vs last month
Revenue
3.1%
▼ 0.4%vs last week
Conversion
1,204
▲ 48since yesterday
Active Users
Spec
typescript
// The description field renders as muted plain text below the label.
// Include the arrow symbol in the string to convey trend direction.
new StatView()
  .stat({ label: 'Revenue',      value: 48290, format: 'currency',
          description: '▲ 12% vs last month' })
  .stat({ label: 'Conversion',   value: 0.031, format: 'percent',
          description: '▼ 0.4% vs last week' })
  .stat({ label: 'Active Users', value: 1204,
          description: '▲ 48 since yesterday' })
  .build()

Page title

Use .title() to render a heading above the grid:

typescript
new StatView()
  .stat({ label: 'Revenue', value: 48290.50, format: 'currency' })
  .title('Dashboard Overview')
  .build();

String values

If your metric is already formatted (e.g. from an external service), pass a string — it renders as-is:

typescript
.stat({ label: 'Uptime', value: '99.98%' })
.stat({ label: 'Build', value: 'Passing' })

Using the standalone renderer

The stat view can be embedded outside the SPA using the renderer bundle:

html
<script src="retrofit-ui.iife.js"></script>

<!-- declarative island — auto-mounted by init() -->
<div data-retrofit='{"kind":"stat","stats":[{"label":"Revenue","value":48290.50,"format":"currency"},{"label":"Users","value":1204}],"metadata":{"title":"Overview"}}'></div>

<!-- or mount explicitly in JS -->
<div id="dashboard"></div>
<script>
  const ui = RetrofitUI.init({ rootElement: document.body, apiBase: '/api' });
  ui.mount(
    {
      kind: 'stat',
      stats: [
        { label: 'Revenue', value: 48290.50, format: 'currency' },
        { label: 'Users', value: 1204 },
      ],
      metadata: { title: 'Overview' },
    },
    document.getElementById('dashboard'),
  );
</script>