Observable Framework View source

Vega-Lite responsive width

The chart below resizes to fit the container. Try resizing the window.

Rather than use Vega-Lite’s built-in responsive width — which only listens to window resize events and doesn’t work correctly when the container is initially detached, or when the page content changes — we use Observable Framework’s built-in resize function which handles all cases thanks to ResizeObserver.

const chart = await vl.render({
  spec: {
    "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
    "width": -1,
    "height": 250,
    "autosize": {"type": "fit", "contains": "padding"},
    "data": {"url": "https://vega.github.io/vega-lite/data/cars.json"},
    "mark": "bar",
    "encoding": {
      "x": {"field": "Cylinders"},
      "y": {"aggregate": "count", "title": "Number of cars"}
    }
  }
});

display(resize((width) => {
  chart.value.width(Math.min(960 - 16 * 2, width));
  chart.value.run();
  return chart;
}));

vlresize(spec)

If you prefer a more reusable solution, you can create a vlresize function like so in a vlresize.js module that you can import into any page.

import {resize} from "npm:@observablehq/stdlib";
import vl from "observablehq:stdlib/vega-lite";

export async function vlresize(
  {autosize = {type: "fit", contains: "padding"}, ...spec},
  {minWidth = 0, maxWidth = Infinity} = {}
) {
  const chart = await vl.render({spec: {...spec, width: -1, autosize}});
  return resize((width) => {
    chart.value.width(Math.max(minWidth, Math.min(maxWidth, width)));
    chart.value.run();
    return chart;
  });
}

You can then import vlresize like so:

import {vlresize} from "./vlresize.js";

And call it like so:

vlresize({
  "height": 250,
  "data": {"url": "https://vega.github.io/vega-lite/data/cars.json"},
  "mark": "bar",
  "encoding": {
    "x": {"field": "Cylinders"},
    "y": {"aggregate": "count", "title": "Number of cars"}
  }
}, {
  maxWidth: 960 - 16 * 2
})

Static width

If you’d prefer to set a fixed width and have the browser scale the chart to fit the container, you can override the default styles that Vega-Lite sets on the canvas element. Below, the natural width of the chart is 640px, but the chart will scale down to fit the container in narrow windows.

const chart = display(await vl.render({
  spec: {
    width: 640,
    height: 250,
    data: {url: "https://vega.github.io/vega-lite/data/cars.json"},
    mark: "bar",
    encoding: {
      x: {field: "Cylinders"},
      y: {aggregate: "count", title: "Number of cars"}
    }
  }
}));

const canvas = chart.firstChild;
canvas.style.aspectRatio = `${canvas.width} / ${canvas.height}`;
canvas.style.maxWidth = "100%";
canvas.style.height = "auto";