# Make a custom chart responsive > Make a custom chart resize with its widget so it fits any dashboard or report, whether you build it in Vega-Lite or Vega. A responsive chart fills its available space and redraws when that space changes: when a user resizes the widget on a dashboard, switches to a phone, or drags the panel divider in a report. A chart with a fixed pixel size, by contrast, stays the same size no matter how much room it has, leaving empty gutters or clipping at the edges. Holistics automatically resizes every custom chart to fit its widget and redraws it when the widget changes, for both Vega-Lite (`@vgl`) and Vega (`@vg`). You do not wire up any resize handling. The work, when there is any, is making sure the chart looks right at every size, which mostly means not pinning its contents to fixed pixel values. The details differ slightly between the two languages. ## Make a Vega-Lite chart responsive Vega-Lite charts are responsive out of the box. Holistics tells Vega-Lite to fit the widget, so as long as you do not hard-code a size, the chart stretches to fill the widget and redraws when it changes. To keep that working: * **Omit `width` and `height` from the spec.** Holistics already fits the chart to the widget. The moment you set `"width": 400`, the chart locks to 400px and stops stretching. The same goes for `height`. Most of the library's `@vgl` charts (like the [diverging bar chart](/docs/charts/custom-charts/library/diverging-bar-chart)) set no size at all. * **Use `"width": "container"` if you copied a sized spec.** Many examples from the [Vega-Lite gallery](https://vega.github.io/vega-lite/examples/) ship with a fixed size. Either delete those properties or set them to `"container"` so the chart reads its widget's dimensions: ```aml {2-3} template: @vgl { "width": "container", "height": "container", "data": { "values": @{values} }, "mark": "bar", "encoding": { ... } };; ``` **Limitation:** Multi-view Vega-Lite layouts (`facet`, `row`, `column`, `concat`, `repeat`) lay out their panels at a natural size and do not stretch to the widget, so they do not fill it. For a responsive grid of small multiples, build it in Vega instead (the [faceted sparkline](/docs/charts/custom-charts/library/faceted-sparkline) does exactly this). ## Make a Vega chart responsive Holistics keeps a `@vg` chart's `width` and `height` signals in sync with the widget (resizing on both window and container changes) and adds an autosize that keeps the axes inside, so the canvas already tracks the widget. The chart relayouts to fit as long as its scales and marks read those signals. It looks stuck only when its scale ranges use fixed numbers, so the marks stay one size while the canvas around them resizes. ### The starting point: a fixed-size chart Here is a plain Vega bar chart whose scales use fixed pixel ranges. Holistics still resizes the canvas to the widget, but the bars stay pinned to 400px wide because the ranges are hard-coded numbers. ```aml {30,38} CustomChartDef responsive_bar { label: 'Responsive Bar' fields { field category { label: 'Category' type: 'dimension' } field value { label: 'Value' type: 'measure' } } template: @vg { "$schema": "https://vega.github.io/schema/vega/v5.json", "width": 400, "height": 300, "data": [ { "name": "table", "values": @{values}, "transform": [ { "type": "formula", "expr": "datum['@{fields.category.name}']", "as": "category" }, { "type": "formula", "expr": "datum['@{fields.value.name}']", "as": "amount" } ] } ], "scales": [ { "name": "x", "type": "band", "domain": { "data": "table", "field": "category" }, "range": [0, 400], "padding": 0.1 }, { "name": "y", "type": "linear", "nice": true, "domain": { "data": "table", "field": "amount" }, "range": [300, 0] } ], "axes": [ { "orient": "bottom", "scale": "x" }, { "orient": "left", "scale": "y" } ], "marks": [ { "type": "rect", "from": { "data": "table" }, "encode": { "update": { "x": { "scale": "x", "field": "category" }, "width": { "scale": "x", "band": 1 }, "y": { "scale": "y", "field": "amount" }, "y2": { "scale": "y", "value": 0 } } } } ] };; } ``` The highlighted lines are the problem: each scale's `range` is a fixed number, so the marks cannot grow. (Holistics overrides the top-level `width`/`height` above, so they do not pin the chart, but it is cleaner to drop them.) ### Make it responsive Bind each scale's `range` to the widget with Vega's `"width"` and `"height"` shorthand instead of fixed numbers: ```aml {6,14} "scales": [ { "name": "x", "type": "band", "domain": { "data": "table", "field": "category" }, "range": "width", "padding": 0.1 }, { "name": "y", "type": "linear", "nice": true, "domain": { "data": "table", "field": "amount" }, "range": "height" } ] ``` `"width"` and `"height"` are the signals Holistics drives from the widget, so the bars now follow it and redraw on resize. (The `"height"` shorthand also flips the y range to run bottom-to-top for you, so larger values sit higher.) You can also drop the top-level `"width"`/`"height"` while you are here, since Holistics overrides them. Save the file and resize the widget on a dashboard to see it reflow. Here is the full definition after both changes:
Complete responsive definition ```aml CustomChartDef responsive_bar { label: 'Responsive Bar' fields { field category { label: 'Category' type: 'dimension' } field value { label: 'Value' type: 'measure' } } template: @vg { "$schema": "https://vega.github.io/schema/vega/v5.json", "data": [ { "name": "table", "values": @{values}, "transform": [ { "type": "formula", "expr": "datum['@{fields.category.name}']", "as": "category" }, { "type": "formula", "expr": "datum['@{fields.value.name}']", "as": "amount" } ] } ], "scales": [ { "name": "x", "type": "band", "domain": { "data": "table", "field": "category" }, "range": "width", "padding": 0.1 }, { "name": "y", "type": "linear", "nice": true, "domain": { "data": "table", "field": "amount" }, "range": "height" } ], "axes": [ { "orient": "bottom", "scale": "x" }, { "orient": "left", "scale": "y" } ], "marks": [ { "type": "rect", "from": { "data": "table" }, "encode": { "update": { "x": { "scale": "x", "field": "category" }, "width": { "scale": "x", "band": 1 }, "y": { "scale": "y", "field": "amount" }, "y2": { "scale": "y", "value": 0 } } } } ] };; } ```
### Adjust the layout with derived signals (optional) The shorthand stretches the plot to the full widget. When you need a margin around the plot, or a layout you compute from the size, you do not read the widget yourself. Holistics already maintains the `width` and `height` signals, so reference them from your own derived signals: ```aml "signals": [ { "name": "plot_width", "update": "width - 50" }, // leave room for a wide y-axis { "name": "cell_width", "update": "width / 3" } // a 3-column small-multiple grid ] ``` Use those signals in your scale ranges or mark positions. The [faceted sparkline](/docs/charts/custom-charts/library/faceted-sparkline) builds its whole grid this way, deriving a per-cell size from `width` and `height`. ## Test that it actually resizes A chart can look fine at its default size and still remain fixed. To confirm it is responsive: 1. Add the chart to a dashboard and drag the widget's corner to resize it. The chart should follow, not leave a gap or clip at the edges. 2. Preview the dashboard at a narrow width (or open it on a phone) and check that nothing overflows horizontally. 3. If the bars or marks do not resize (in `@vg`), look for scale ranges or mark sizes that use fixed numbers instead of the `"width"`/`"height"` shorthand. ## Next steps - [Style a custom chart](/guides/style-custom-charts) to match the look of Holistics's built-in charts. - [Make a custom chart interactive](/guides/create-interactive-custom-charts) so users can click and drag to filter. - Browse the [Custom Chart Library](/docs/charts/custom-charts/library) for ready-made responsive templates you can copy.