Charts
Data visualization with Unovis — a framework-agnostic charting library.
Why Unovis
Haze Dashboard uses Unovis for all charts and data visualizations. Unovis is framework-agnostic — the same data models and accessor functions work across @unovis/react, @unovis/svelte, and @unovis/angular. This makes your chart code portable if you ever switch frameworks.
Both @unovis/ts (core) and
SSR: ClientOnly Wrapper
Unovis renders to SVG/Canvas and requires the DOM. Since Nuxt renders pages on the server first (SSR), you must wrap all charts in a <ClientOnly> component to prevent server-side rendering errors:
<template>
<ClientOnly>
<VisXYContainer :data="data" :height="300">
<VisLine :x="(d) => d.month" :y="(d) => d.revenue" />
<VisAxis type="x" />
<VisAxis type="y" />
</VisXYContainer>
<template #fallback>
<div class="h-[300px] animate-pulse rounded-lg bg-[var(--haze-surface-hover)]" />
</template>
</ClientOnly>
</template> The #fallback slot provides a placeholder skeleton while the chart loads on the client. This prevents layout shifts and gives users immediate visual feedback.
Available Chart Types
| Type | Components | Use Case |
|---|---|---|
| Line | VisXYContainer + VisLine | Trends over time (revenue, traffic) |
| Area | VisXYContainer + VisArea | Filled trend lines (growth, cumulative) |
| Bar | VisXYContainer + VisGroupedBar | Category comparison (sales by region) |
| Stacked Bar | VisXYContainer + VisStackedBar | Part-to-whole by category |
| Donut | VisSingleContainer + VisDonut | Proportions (device breakdown, category split) |
| Scatter | VisXYContainer + VisScatter | Correlation analysis |
Example: Line Chart
A complete line chart with X and Y axes. The :x and :y props accept accessor functions that map your data shape to chart coordinates:
<template>
<ClientOnly>
<VisXYContainer :data="data" :height="300">
<VisLine
:x="(d, i) => i"
:y="(d) => d.revenue"
color="var(--haze-primary)"
:curveType="'monotoneX'"
/>
<VisAxis type="x" :tickFormat="(i) => months[i]" />
<VisAxis type="y" :tickFormat="(v) => '$' + v" />
</VisXYContainer>
</ClientOnly>
</template>
<script setup lang="ts">
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
const data = [
{ month: 'Jan', revenue: 4200 },
{ month: 'Feb', revenue: 5800 },
{ month: 'Mar', revenue: 7200 },
{ month: 'Apr', revenue: 6100 },
{ month: 'May', revenue: 8400 },
{ month: 'Jun', revenue: 9200 },
]
</script>Example: Donut Chart
Donut charts use VisSingleContainer instead of VisXYContainer:
<template>
<ClientOnly>
<VisSingleContainer :data="data" :height="250">
<VisDonut
:value="(d) => d.value"
:arcWidth="40"
:padAngle="0.02"
:color="(d, i) => colors[i]"
/>
</VisSingleContainer>
</ClientOnly>
</template>
<script setup lang="ts">
const colors = ['#14b8a6', '#3b82f6', '#f97316', '#8b5cf6']
const data = [
{ label: 'Desktop', value: 62 },
{ label: 'Mobile', value: 28 },
{ label: 'Tablet', value: 7 },
{ label: 'Other', value: 3 },
]
</script>Example: Area Chart
Area charts work exactly like line charts but fill the area below the line. You can combine multiple area layers for comparison:
<template>
<ClientOnly>
<VisXYContainer :data="data" :height="250">
<VisArea
:x="(d, i) => i"
:y="(d) => d.users"
color="var(--haze-primary)"
:opacity="0.3"
:curveType="'monotoneX'"
/>
<VisLine
:x="(d, i) => i"
:y="(d) => d.users"
color="var(--haze-primary)"
/>
<VisAxis type="x" />
<VisAxis type="y" />
</VisXYContainer>
</ClientOnly>
</template>
<script setup lang="ts">
const data = [
{ users: 120 },
{ users: 180 },
{ users: 240 },
{ users: 310 },
{ users: 280 },
{ users: 390 },
]
</script>Accessor Functions
Unovis uses accessor functions to decouple chart components from your data shape. Instead of requiring specific property names, you provide a function that extracts the value:
// Accessor functions extract values from your data const xAccessor = (d: DataPoint, i: number) => i // Use index as x const yAccessor = (d: DataPoint) => d.revenue // Use revenue as y const colorAccessor = (d: DataPoint) => d.color // Per-item color // These are passed as props: // <VisLine :x="xAccessor" :y="yAccessor" :color="colorAccessor" />
This pattern means you can feed any data structure to the chart — just update the accessor functions.
Styling Charts
Use the design system's CSS custom properties for consistent chart colors. Unovis accepts color values as props or via CSS:
<!-- Use CSS variables for consistent theming --> <VisLine :x="(d, i) => i" :y="(d) => d.value" color="var(--haze-primary)" /> <!-- Multiple series with different colors --> <VisLine :x="xAccessor" :y="(d) => d.revenue" color="#14b8a6" /> <VisLine :x="xAccessor" :y="(d) => d.expenses" color="#f97316" />
Charts automatically resize to fit their container. Ensure the parent element has a defined height (either via the container's :height prop or a CSS class).
Adding a Chart to a Dashboard Page
Follow these steps to add a new chart:
- Prepare your data array (from an API, store, or static data)
- Define accessor functions for the x and y axes
- Wrap the chart in
<ClientOnly> - Set a height on the container
- Add axes with
VisAxisif needed
Tip
Unovis configs are portable — the same data structures and accessor functions work identically in @unovis/react, @unovis/svelte, and @unovis/angular. Only the template syntax changes.
Next Steps
Learn about multi-language support in the Internationalization guide.