# making vega-lite plots responsive

**URL:** <https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247>\
**Category:** Help\
**Created:** [May 10, 2024, 1:37pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247 "2024-05-10T13:37:22Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![rjbudzynski](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/rjbudzynski/32/8878_2.png) [@rjbudzynski](https://talk.observablehq.com/u/rjbudzynski)\
**Post date:** [May 10, 2024, 1:37pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/1 "2024-05-10T13:37:22Z")

</div>

I’m having trouble making vega-lite plots adapt to the size of the browser viewport. The only thing that works reliably to set the plot physical size is to set the width and height properties to specific values in the plot spec; but then, if the browser viewport is too small I don’t even get a horizontal scrollbar, the plot stays at its fixed size and just gets cut off.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 10, 2024, 5:59pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/2 "2024-05-10T17:59:07Z")

</div>

Not sure if you’re using notebooks or Observable Framework, but here’s how I would do it in Framework:

```js
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": "Origin"},
      "y": {"aggregate": "count", "title": "Number of Cars"}
    }
  }
});

display(resize((width) => {
  chart.value.width(width);
  chart.value.run();
  return chart;
}));

```

View live: [Vega-Lite responsive bar chart | Observable Framework](https://observablehq.observablehq.cloud/framework-example-vega-responsive/)

Unfortunately the `"width": "container"` built-in to Vega-Lite doesn’t work because it’s limited to _resize_ events on the window rather than using ResizeObserver, but I filed a [feature request for the latter](https://github.com/vega/vega-lite/issues/9349) so maybe it’ll get better in the future.

---

<div class="post-metadata">

**Author:** ![rjbudzynski](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/rjbudzynski/32/8878_2.png) [@rjbudzynski](https://talk.observablehq.com/u/rjbudzynski)\
**Post date:** [May 10, 2024, 7:42pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/3 "2024-05-10T19:42:04Z")

</div>

Thanks for responding. Yes, I was frustrated with `width: "container"` not working.  
Is there any way to use this code in a js module where I put functions that generate my plots (essentially from a couple of templates), so I won’t have to repeat the `display()` call verbatim for each plot?

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 11, 2024, 1:11am UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/4 "2024-05-11T01:11:57Z")

</div>

Sure, you can do something like this:

```js
async function vlresize(spec) {
  const chart = await vl.render({spec});
  return resize((width) => {
    chart.value.width(width);
    chart.value.run();
    return chart;
  });
}

```

And then to use:

```js
vlresize({
  "$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": "Origin"},
    "y": {"aggregate": "count", "title": "Number of Cars"}
  }
})

```

---

<div class="post-metadata">

**Author:** ![rjbudzynski](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/rjbudzynski/32/8878_2.png) [@rjbudzynski](https://talk.observablehq.com/u/rjbudzynski)\
**Post date:** [May 11, 2024, 1:00pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/5 "2024-05-11T13:00:19Z")

</div>

But the `vlresize()` definition must be copied into each md file that needs it, rather than be imported from a module - otherwise I will get `resize is not a function`, right? Or am I missing something?

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 11, 2024, 4:21pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/6 "2024-05-11T16:21:46Z")

</div>

You can import `resize` from the Observable standard library. So you can make a `vlresize.js` module like this (I also added the default `autosize` and initial `width` for convenience):

```js
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;
  });
}

```

And then import it into your page like this:

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

```

And call it like this:

```js
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"}
  }
})

```

---

<div class="post-metadata">

**Author:** ![rjbudzynski](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/rjbudzynski/32/8878_2.png) [@rjbudzynski](https://talk.observablehq.com/u/rjbudzynski)\
**Post date:** [May 11, 2024, 7:57pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/7 "2024-05-11T19:57:54Z")

</div>

Yes, I’ve been trying something along these lines. But, for some reason it does not work for me, _unless_ I wrap the plot in a `<div style:"width=100%">`. Without the div the plot stays at the vega-lite default width afaics. With the div it works as expected.

BTW, why the two different forms of sources in the imports at the top of your example?

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 11, 2024, 9:42pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/8 "2024-05-11T21:42:51Z")

</div>

Hard to say without seeing the code. Maybe you could share a reproduction? Be aware that paragraph (`p`) elements have a default max-width of 640px. But as you can see from the live example, the code I posted works as advertised. I guess I forgot to mention I updated the live example here:

[https://observablehq.observablehq.cloud/framework-example-vega-responsive/](https://observablehq.observablehq.cloud/framework-example-vega-responsive/)

What do you mean by different forms of imports? The first `npm:` import is how you import from npm in Framework. (You can also use bare module specifiers if you want to import from `node_modules`, but then you’ll have to install things yourself using a package manager rather than letting Framework install it for you on use.) You can use an `npm:` import for Vega-Lite too, it’s just a bit more verbose because you need to import three separate things and string them together like so:

```js
import * as vega from "npm:vega";
import * as vegaLite from "npm:vega-lite";
import * as vegaLiteApi from "npm:vega-lite-api";

const vl = vegaLiteApi.register(vega, vegaLite);

```

This is described here:

[https://observablehq.com/framework/lib/vega-lite](https://observablehq.com/framework/lib/vega-lite)

I didn’t want to repeat this boilerplate so I cheated a little and used the definition of the `vl` built-in supplied by the Observable standard library, which is defined here:

> <https://github.com/observablehq/framework/blob/main/src/client/stdlib/vega-lite.js>

They’re exactly equivalent so use whichever you prefer.

---

<div class="post-metadata">

**Author:** ![rjbudzynski](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/rjbudzynski/32/8878_2.png) [@rjbudzynski](https://talk.observablehq.com/u/rjbudzynski)\
**Post date:** [May 11, 2024, 10:08pm UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/9 "2024-05-11T22:08:37Z")

</div>

Okay, thanks for clarifying. So, how do you make your example plot not be a part of a `<p>` element? Other than wrapping it in some other html? Nb. the _view-source_ link in your example doesn’t work (404).  
What I meant about the imports is that `import { resize } from "observablehq:stdlib"`works just as well, so I was wondering why not write it like that.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 12, 2024, 2:52am UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/10 "2024-05-12T02:52:35Z")

</div>

I use a fenced code block (````js`) rather than an inline expression (`${…}`).

If you want to use a top-level inline expression and you don’t want it to be in a paragraph, then you must make an HTML block in Markdown by wrapping it in some HTML such as a `<div>` tag. So this is implicitly a paragraph:

```md
${…}

```

But this is a DIV:

```md
<div>${…}</div>

```

Of course if you don’t need anything around your content then I’d just use a fenced code block (````js`).

Regarding importing `observablehq:stdlib`, that’s not documented and currently intended for internal use. The documented way to import the Observable standard library is to import `npm:@observablehq/stdlib`. Both will work though, and are equivalent. Maybe we’ll formally support `observablehq:` in the future to represent these built-in libraries.

Our intent here was to favor a more standard way of importing the libraries so that you can import _e.g._ `@observablehq/stdlib` in a Node.js program and it works the same way. But we have an outstanding issue [#852](https://github.com/observablehq/framework/issues/852) where we haven’t actually published a number of these libraries to npm yet…

---

<div class="post-metadata">

**Author:** ![rjbudzynski](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/rjbudzynski/32/8878_2.png) [@rjbudzynski](https://talk.observablehq.com/u/rjbudzynski)\
**Post date:** [May 12, 2024, 8:21am UTC](https://talk.observablehq.com/t/making-vega-lite-plots-responsive/9247/11 "2024-05-12T08:21:42Z")

</div>

Great, that pretty much explains all I was missing. When I have further questions (and I’m sure I will), I guess it would make more sense to open another thread. Thanks again.
