Skip to content

VegaLiteChart ​

Renders a Vega-Lite spec with the native cfasim charts: a spec with a bar mark becomes a BarChart, anything else a LineChart, so the chart gets the same styling, tooltips, CSV download, zoom and dark mode as the rest of the app. Specs from Altair (chart.to_dict()), Observable or an LLM work as long as they stay in the supported subset. There is no Vega dependency: the spec is translated, not run.

It lives at its own entry point, so apps that never use it pay nothing:

ts
import { VegaLiteChart, vegaLiteToChart } from "@cfasim-ui/charts/vega-lite";

Examples ​

Line chart ​

A color field splits the rows into one series per value, colored in the order Vega-Lite sorts the field's values. interpolate sets the line curve, and axis.format takes a d3 format string.

Weekly casesNorthSouth1020304050CasesWeekJan 05Jan 19Feb 02Feb 16
vue
<script setup>
import { VegaLiteChart } from "@cfasim-ui/charts/vega-lite";

const spec = {
  title: "Weekly cases",
  data: { values: weekly }, // [{ week: "2026-01-05", region: "North", cases: 12 }, ...]
  mark: { type: "line", interpolate: "monotone", point: true },
  encoding: {
    x: {
      field: "week",
      type: "temporal",
      title: "Week",
      axis: { format: "%b %d" },
    },
    y: { field: "cases", type: "quantitative", title: "Cases" },
    color: { field: "region", type: "nominal" },
  },
};
</script>

<template>
  <VegaLiteChart :spec="spec" :height="260" />
</template>

Layers: band, line and threshold ​

Layers share the data and encodings of the spec that holds them. An area with y and y2 is a band; a rule with only a y datum is a threshold across the plot, and the y axis grows to keep it in view.

102030405060CasesWeekJan 05Jan 19Feb 02Feb 16
ts
const spec = {
  data: { values: north },
  encoding: {
    x: {
      field: "week",
      type: "temporal",
      title: "Week",
      axis: { format: "%b %d" },
    },
  },
  layer: [
    {
      mark: { type: "area", opacity: 0.25 },
      encoding: {
        y: { field: "lo", type: "quantitative", title: "Cases" },
        y2: { field: "hi" },
      },
    },
    { mark: "line", encoding: { y: { field: "cases", type: "quantitative" } } },
    {
      mark: { type: "rule", color: "#dc2626", strokeDash: [4, 4] },
      encoding: { y: { datum: 50 } },
    },
  ],
};

Stacked bars ​

Colored bars stack by default, as in Vega-Lite. Set xOffset to the color field to group them side by side, or stack: null on y to overlay them.

VaccinatedUnvaccinated0%50%100%ShareAge group0-1718-4950-6465+
ts
const spec = {
  data: { values: ages }, // [{ age: "0-17", status: "Vaccinated", share: 0.42 }, ...]
  mark: "bar",
  encoding: {
    x: { field: "age", type: "ordinal", title: "Age group" },
    y: {
      field: "share",
      type: "quantitative",
      title: "Share",
      axis: { format: ".0%" },
    },
    color: {
      field: "status",
      type: "nominal",
      scale: {
        domain: ["Vaccinated", "Unvaccinated"],
        range: ["#2563eb", "#cbd5e1"],
      },
    },
  },
};

Scatter with size and color ​

size, opacity and a quantitative color encode more fields on point marks. Size is an area, as in Vega-Lite; a continuous color runs between the two colors of scale.range.

2025303540Peak cases (%)R₀1.522.53
ts
const spec = {
  data: { values: runs }, // [{ r0: 1.2, peak: 19, attack: 42 }, ...]
  mark: "circle",
  encoding: {
    x: { field: "r0", type: "quantitative", title: "R₀" },
    y: { field: "peak", type: "quantitative", title: "Peak cases (%)" },
    size: { field: "attack", type: "quantitative" },
    color: {
      field: "attack",
      type: "quantitative",
      scale: { range: ["#bfdbfe", "#1e3a8a"] },
    },
  },
};

Data and chart props ​

Pass rows with the data prop to keep the spec separate from its data (it wins over data.values, and is the way to feed a spec whose data is a url). Every other attribute is a prop of the chart that renders, merged over what the spec sets, and slots such as tooltip pass through:

vue
<VegaLiteChart :spec="spec" :data="rows" zoom legend-toggle :height="320" />

Unsupported pieces ​

Anything outside the supported subset is skipped and the rest renders. The component logs each skipped piece with console.warn and emits them as warnings; with strict it renders nothing and emits error instead. Data must arrive ready to plot: transforms and aggregates are not applied.

20304050CasesWeekJan 5Jan 19Feb 2Feb 16
vue
<!-- Warns that the `transform` and `x.timeUnit` are not applied. -->
<VegaLiteChart :spec="spec" @warnings="(w) => (skipped = w)" />

vegaLiteToChart ​

The translation on its own, for rendering the chart yourself or adjusting its props first. It returns which chart to render, its props, and the warnings; strict: true throws on the first unsupported piece instead.

ts
import { vegaLiteToChart } from "@cfasim-ui/charts/vega-lite";
import { LineChart, BarChart } from "@cfasim-ui/charts";

const { component, props, warnings } = vegaLiteToChart(spec, { data: rows });
// <component :is="component === 'BarChart' ? BarChart : LineChart" v-bind="props" />

Supported subset ​

SpecSupported
Top leveltitle (text, multi-line), numeric width / height, inline data.values, layer (nested, inheriting data and encodings)
Marksline, trail, point, circle, square, area, bar, rule, errorbar, errorband, text
Mark propertiescolor, opacity, strokeWidth, strokeDash, interpolate (linear, step, step-before, step-after, monotone), point: true, filled, size
x / yquantitative, temporal, nominal and ordinal fields; datum for rules and text; x2 / y2; bin: "binned" bars
colora nominal or ordinal field (one series per value; scale.domain / scale.range), a constant value, a quantitative field on point marks
size, opacityfields on point marks, value
xOffset / yOffsetgroups bars side by side
texta field or value on text marks
Stackingstacked bars and areas by color (default), stack: "normalize", stack: null
sortarrays, "ascending" / "descending", null, and channel sorts like "-y" (by each value's sum)
scaletype: "log" on the value axis, domain
axistitle, format (d3), formatType: "time", grid, axis: null for a bar value axis
Not supported (each warned)transform, aggregate, timeUnit, bin: true, url data without the data prop, config, params / selections, facet / repeat / concat, color schemes, other marks, interpolations and scale types

The chart applies its own styling: Vega-Lite's default grid lines, axis padding and color scheme are not copied.

Props ​

PropTypeRequiredDefault
specVegaLiteSpecYes—
datareadonly Record&lt;string, unknown&gt;[]No—
strictbooleanNo—