DOM Studio
Data Visualisation
Charts & reportingTurn application data into clear charts. Consistent colours, useful defaults, and examples you can own and adapt.
Live example
A complete reporting surface
Choose a date range to update the traffic chart and its summary metrics. The lower cards show separate full-month reports.
Traffic overview
Example workspace · September 2026
Reporting period
September 2026
September 2026
Visitors
14,114
Organic share
67%
Days in view
30
2026-09-01 to 2026-09-30 · Daily visitors
View data for Audience growth
| date | Organic search | Direct |
|---|---|---|
| 1 Sept | 210 | 142 |
| 2 Sept | 259 | 133 |
| 3 Sept | 288 | 109 |
| 4 Sept | 287 | 90 |
| 5 Sept | 260 | 93 |
| 6 Sept | 222 | 118 |
| 7 Sept | 195 | 148 |
| 8 Sept | 195 | 163 |
| 9 Sept | 225 | 153 |
| 10 Sept | 274 | 129 |
| 11 Sept | 323 | 111 |
| 12 Sept | 351 | 115 |
| 13 Sept | 350 | 140 |
| 14 Sept | 322 | 170 |
| 15 Sept | 284 | 184 |
| 16 Sept | 258 | 174 |
| 17 Sept | 258 | 150 |
| 18 Sept | 289 | 132 |
| 19 Sept | 338 | 136 |
| 20 Sept | 387 | 161 |
| 21 Sept | 414 | 191 |
| 22 Sept | 412 | 205 |
| 23 Sept | 384 | 195 |
| 24 Sept | 346 | 170 |
| 25 Sept | 320 | 152 |
| 26 Sept | 322 | 157 |
| 27 Sept | 353 | 183 |
| 28 Sept | 402 | 213 |
| 29 Sept | 450 | 226 |
| 30 Sept | 478 | 215 |
Full September · GBP
View data for Revenue by plan
| plan | New accounts | Recurring |
|---|---|---|
| Starter | £4,800 | £2,200 |
| Growth | £7,400 | £9,800 |
| Scale | £6,200 | £15,400 |
Full September · All channels
View data for Acquisition channels
| Category | Value |
|---|---|
| 8,240 | |
| 3,620 | |
| 2,180 | |
| 1,460 |
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { computed, ref } from 'vue';
import { DomAreaChart, DomBarChart, DomCard, DomDateRangePicker, DomDonutChart, DomMetricCard } from '@getdom/studio';
const period = ref({ start: '2026-09-01', end: '2026-09-30' });
const comparison = ref('none');
// Deterministic sample data keeps the example identical during SSR and hydration.
const dailyRows = Array.from({ length: 30 }, (_, index) => ({
date: `2026-09-${String(index + 1).padStart(2, '0')}`,
organic: Math.round(210 + index * 7 + Math.sin(index * 0.7) * 65),
direct: Math.round(110 + index * 3 + Math.cos(index * 0.9) * 32),
}));
const sources = [{ label: 'Organic search', value: 8240, color: 'chart-1' }, { label: 'Direct', value: 3620, color: 'chart-2' }, { label: 'Referral', value: 2180, color: 'chart-3' }, { label: 'Email', value: 1460, color: 'chart-4' }];
const plans = [{ plan: 'Starter', new: 4800, recurring: 2200 }, { plan: 'Growth', new: 7400, recurring: 9800 }, { plan: 'Scale', new: 6200, recurring: 15400 }];
const trafficSeries = [{ key: 'organic', label: 'Organic search', color: 'chart-1' }, { key: 'direct', label: 'Direct', color: 'chart-2' }];
const revenueSeries = [{ key: 'new', label: 'New accounts', color: 'chart-1' }, { key: 'recurring', label: 'Recurring', color: 'chart-2' }];
const selectedRows = computed(getSelectedRows);
const visitorRows = computed(() => selectedRows.value.map((row) => ({ date: row.date, value: row.organic + row.direct })));
const visitors = computed(() => selectedRows.value.reduce((total, row) => total + row.organic + row.direct, 0));
const organic = computed(() => selectedRows.value.reduce((total, row) => total + row.organic, 0));
const description = computed(() => `${period.value.start} to ${period.value.end} · Daily visitors`);
/** @returns {Array<object>} Filters application records using inclusive reporting dates. */
function getSelectedRows() { return dailyRows.filter((row) => row.date >= period.value.start && row.date <= period.value.end); }
</script>
<template>
<div class="w-full space-y-5">
<div class="flex flex-wrap items-center justify-between gap-4">
<div><p class="text-sm font-semibold">Traffic overview</p><p class="mt-1 text-xs text-muted-fg">Example workspace · September 2026</p></div>
<DomDateRangePicker v-model="period" v-model:comparison="comparison" :show-comparison="false" anchor-date="2026-09-30" min="2026-09-01" max="2026-09-30" />
</div>
<div class="grid gap-4 sm:grid-cols-3">
<DomMetricCard label="Visitors" :value="visitors" :rows="visitorRows" color="chart-1" />
<DomMetricCard label="Organic share" :value="visitors ? organic / visitors : null" format="percent" />
<DomMetricCard label="Days in view" :value="selectedRows.length" />
</div>
<DomCard padding="sm"><DomAreaChart title="Audience growth" :description="description" :rows="selectedRows" x-key="date" x-type="time" :series="trafficSeries" stacked /></DomCard>
<div class="grid gap-4 xl:grid-cols-2">
<DomCard padding="sm"><DomBarChart title="Revenue by plan" description="Full September · GBP" :rows="plans" x-key="plan" :series="revenueSeries" format="currency" :height="240" /></DomCard>
<DomCard padding="sm"><DomDonutChart title="Acquisition channels" description="Full September · All channels" :rows="sources" :height="240" total-label="Visitors" /></DomCard>
</div>
</div>
</template>
Explore
Charts and reporting components
Line chart
DomLineChartTrends, comparisons, and targets. Category, time, and numeric axes.
Area chart
DomAreaChartVolume over time, overlapping series, and stacked composition.
Bar chart
DomBarChartGrouped, stacked, percentage, and horizontal comparisons.
Donut chart
DomDonutChartCategory shares, centre totals, and selectable legends.
Sparkline
DomSparklineCompact trends inside cards, tables, and resource lists.
Metric card
DomMetricCardValues, units, comparison periods, and optional sparklines.
Date range picker
DomDateRangePickerReporting presets, custom dates, and comparison periods.
Application examples
Finance
Finance & cashflow
Investment portfolios, bank money in and out, cash projections, and a simulated transaction feed that updates the reports and charts.
Working with data
One chart language
Your records, directly
Pass rows, an xKey, and series definitions. Applications own fetching, aggregation, timezone policy, and comparisons. Missing measurements remain distinct from zero.
Styled with your theme
Chart colours use --chart-1 through --chart-5. Axes, grid lines, tooltips, and legends use the surrounding theme. Assign explicit colours to coordinate multiple charts.
More than a picture
Use the keyboard to inspect points, toggle legends, and open the original data table. Line, area, and bar charts emit the original record when a point is selected.
Custom compositions
Built on Unovis
The chart family uses Unovis 1.7. DOM Studio supplies the record mapping, theme integration, shared states, and reporting controls. Import DomChartContainer, DomChartLegend, and DomChartTooltip when composing a custom visualisation with Unovis. The standard charts load their drawing engine after mount; headings and data tables are available during server rendering.
Unovis documentation