---
url: https://chartbuddy.io/embed/docs/guides/vue.md
---
> Docs index: [llms.txt](https://chartbuddy.io/embed/docs/llms.txt)
# Vue
```bash
npm install @chartbuddy.io/embed vue
```
`vue` is an optional peer dependency — install it only if you use these bindings.
Vue 3.
```vue
```
The chart fills its container, so **give the container a height**. A container
with no height renders nothing visible.
## Without a bundler (CDN / single HTML file)
The bindings import `vue` as a bare specifier, so a plain HTML page needs an
**import map**. Use the browser ESM build of Vue, and render without SFCs:
```html
```
The import map must map **both** `vue` and `@chartbuddy.io/embed/vue`, and the
subpath needs the explicit `.mjs` file — bare-specifier subpath resolution does
not work in browsers.
For a standalone artifact with no framework at all, the
[custom element](https://chartbuddy.io/embed/docs/guides/angular.md#other-frameworks) needs no import map.
## Why use the wrapper
`new Insight()` owns a DOM node and lives across renders. The wrapper handles the
lifecycle so you don't:
* **Data changes patch instead of remount.** A new `chartData` calls `update()` on
the live instance. Rebuilding the chart on every change makes an embedded
editor feel broken.
* **Only construction options remount.** Changing `editable` or `instanceId`
rebuilds the chart; everything else is applied in place.
* **Reactive proxies never reach the engine.** The wrapper passes `toRaw()`
config, so Vue's proxy wrapper doesn't leak into chart internals.
* **Unmount destroys.** Listeners and the instance are cleaned up on
`onBeforeUnmount`.
## Editable charts with two-way data
```vue
```
`change` fires after live edits in edit mode, after programmatic updates, and on
Done. Feeding it straight back into `chartData` is safe — the wrapper compares by
identity and the object it hands you is the one it already applied.
## Assign, don't mutate
`chartData` is compared **by identity**, not deeply. Mutating a nested property in
place will not be detected:
```js
// Not picked up — same object identity
chartData.value.title.text = 'FY26';
// Picked up
chartData.value = { ...chartData.value, title: { text: 'FY26' } };
```
## Events
| Event | Payload |
|---|---|
| `ready` | The `Insight` instance |
| `change` | `ChartData \| null` |
| `mode` | `'view' \| 'edit'` |
| `error` | `Error` — a `ChartDataValidationError` for invalid config |
## `useInsight` for imperative access
When you need the instance itself — PNG export, edit mode, revision tracking —
use the composable and place the container yourself.
```vue
{{ error.message }}
```
`chartData` accepts a plain object, a `ref`, or a getter. `insight` is a
`shallowRef` holding `null` until the mount resolves — guard imperative calls on
`ready`.
## What remounts, what patches
| Prop change | Effect |
|---|---|
| `chartData` | `update()` on the live instance |
| `instanceId`, `editable`, `assetBase`, `hostOverrides` | Full remount (destroy + construct) |
| Anything else in the parent | Nothing |
Toggling `editable` remounting is deliberate: view mode and the full editor are
different mounts. If you toggle it often, prefer one mount plus
`insight.enterEditMode()` / `exitEditMode()`.
## Handling invalid data
Chart data is validated at the API boundary, so bad data fails at mount. The
wrapper surfaces it rather than throwing through your render:
```vue
```
See [Validation](https://chartbuddy.io/embed/docs/quality-assurance/validation.md).
## Dashboards
One `` per chart, each with a stable `instance-id`. Duplicate ids on
a page throw, so derive them from your data rather than the loop index if the
list can reorder.
```vue
```
The engine loads once per page, not once per chart — but read
[Installation](https://chartbuddy.io/embed/docs/getting-started/installation.md#bundle-size-and-many-charts) before
putting a lot of charts on one screen.
## Nuxt / SSR
The bindings are browser-only: the chart engine needs a real DOM. The mount
happens in `onMounted`, so nothing runs during SSR, but wrap usage in
`` so Nuxt does not try to render it on the server:
```vue
```
There is no headless/Node render path today, so charts cannot be pre-rendered to
PNG on a server.