Customization

Charts

Data visualization with Unovis — a framework-agnostic charting library.

Why Unovis

Haze Dashboard uses Unovis for all charts. 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.

SSR: Disable Server Rendering

Unovis renders to SVG/Canvas and requires the DOM. Since Next.js renders pages on the server by default, you must either mark chart components 'use client' and import them via next/dynamic with ssr: false, or lazy-load them at runtime:

'use client'
import dynamic from 'next/dynamic'

// Lazy-load chart components (unovis needs the DOM)
const RevenueChart = dynamic(() => import('./RevenueChart'), {
  ssr: false,
  loading: () => (
    <div className="h-[300px] animate-pulse rounded-lg bg-[var(--haze-surface-hover)]" />
  ),
})

export default function Dashboard() {
  return <RevenueChart />
}

The loading placeholder prevents layout shifts and gives users immediate visual feedback while the chart hydrates.

Available Chart Types

TypeComponentsUse Case
LineVisXYContainer + VisLineTrends over time
AreaVisXYContainer + VisAreaFilled trend lines
BarVisXYContainer + VisGroupedBarCategory comparison
Stacked BarVisXYContainer + VisStackedBarPart-to-whole by category
DonutVisSingleContainer + VisDonutProportions
ScatterVisXYContainer + VisScatterCorrelation 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:

'use client'
import { VisXYContainer, VisLine, VisAxis } from '@unovis/react'

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 },
]

export default function RevenueChart() {
  return (
    <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 as number]} />
      <VisAxis type="y" tickFormat={(v) => '$' + v} />
    </VisXYContainer>
  )
}

Example: Donut Chart

Donut charts use VisSingleContainer instead of VisXYContainer:

'use client'
import { VisSingleContainer, VisDonut } from '@unovis/react'

const colors = ['#14b8a6', '#3b82f6', '#f97316', '#8b5cf6']

const data = [
  { label: 'Desktop', value: 62 },
  { label: 'Mobile', value: 28 },
  { label: 'Tablet', value: 7 },
  { label: 'Other', value: 3 },
]

export default function TrafficDonut() {
  return (
    <VisSingleContainer data={data} height={250}>
      <VisDonut
        value={(d) => d.value}
        arcWidth={40}
        padAngle={0.02}
        color={(_d, i) => colors[i]}
      />
    </VisSingleContainer>
  )
}

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:

'use client'
import { VisXYContainer, VisArea, VisLine, VisAxis } from '@unovis/react'

const data = [
  { users: 120 }, { users: 180 }, { users: 240 },
  { users: 310 }, { users: 280 }, { users: 390 },
]

export default function UsersChart() {
  return (
    <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>
  )
}

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} />

Styling Charts

Use the design system's CSS custom properties for consistent chart colors:

// 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" />

Adding a Chart to a Page

Follow these steps to add a new chart:

  1. Prepare your data array (from an API route, server component, or static data)
  2. Define accessor functions for the x and y axes
  3. Create a client component ('use client') for the chart
  4. Lazy-load it from the parent page via next/dynamic with ssr: false
  5. Set a height on the container

Tip

Unovis configs are portable — the same data structures and accessor functions work identically in @unovis/react, @unovis/svelte, and @unovis/angular. Only the JSX syntax changes.

Next Steps

Learn about multi-language support in the Internationalization guide.