Guides

Events and v-model

Listen to chart interaction and control brush, tooltip and legend state

Events

All callbacks are typed Vue events, so your editor completes their names and payloads.

<BarChart :data="data" @click="(state, event) => console.log(state.activeIndex)">
  <Bar data-key="desktop" @click="(entry, index, event) => select(entry.payload)" />
</BarChart>
ComponentEventsPayload
Chart containers and standalone chartsclick, mouseenter, mousemove, mouseleave, mousedown, mouseup, contextmenu, dblclick, touchstart, touchmove, touchend(state: ChartPointerState, event)
Series (Bar, Line, Area, Scatter, Pie, Radar, RadialBar, Funnel)click, mouseenter, mouseleave(entry, index, event)
Series, Treemap, Sankey, SunburstChart, JourneySankey, cell charts, Sparkline, BarListanimation-start, animation-endnone
Bar, Pie, Tooltip, Sparkline, cell chartsupdate:activeIndex(index: number | null)
Brushupdate:range(range: BrushStartEndIndex | null)
Brushchange, drag-end({ startIndex, endIndex })
Legendclick, mouseenter, mouseleave(entry, index, event)
Legendupdate:hidden(hidden: string[])
Legendbbox-update(box: { width, height } | null)
Treemap, SunburstChartnode-click, node-mouseenter, node-mouseleave(node, index, event)
Sankey, JourneySankeynode-click, node-mouseenter, node-mouseleave, link-click, link-mouseenter, link-mouseleave(item, index, event)
JourneySankeyupdate:pinned(path: string[] | null)
Cell charts (Tracker, Heatmap, CohortChart, CalendarHeatmap)cell-click, cell-mouseenter, cell-mouseleave(cell, index, event); cell is the chart's domain object
BarListrow-click(row, index, event)

node-click, cell-click and the JourneySankey link-click also fire when the user presses Enter on the focused item, so their event can be a KeyboardEvent.

v-model

Bind a model when the rest of your page needs to read or set chart state. Without a model, the chart manages the state itself.

Brush range

<script setup lang="ts">
import type { BrushStartEndIndex } from 'vccs'

const range = ref<BrushStartEndIndex | null>({ startIndex: 0, endIndex: 29 })
</script>

<template>
  <BarChart
    :data="data"
    :height="300"
  >
    <Bar data-key="desktop" />
    <Brush
      v-model:range="range"
    />
  </BarChart>
  <p v-if="range">
    Showing rows {{ range.startIndex }}–{{ range.endIndex }}
  </p>
</template>

null shows all rows without travellers. Brush requests a normalized range when bounds fall outside the current data. A parent can accept or reject each request.

Active element

Highlight the same row in a table and in the chart:

<script setup lang="ts">
const active = ref<number | null>(null)
</script>

<template>
  <LineChart
    :data="data"
    :height="300"
  >
    <Line data-key="desktop" />
    <Tooltip v-model:active-index="active" />
  </LineChart>
  <tr
    v-for="(row, i) in data"
    :key="row.date"
    :class="{ highlight: i === active }"
    @mouseenter="active = i"
  >
    ...
  </tr>
</template>

null hides the tooltip. If you omit active-index, the chart manages selection. If you supply it, including null, pointer, keyboard and sync events request a change through update:activeIndex. The displayed selection changes only when you accept that request.

In axis mode, the Tooltip index addresses a chart data position. In item mode, it addresses the keyboard order: series in registration order, then each series' data order. Treemap uses leaves in row order, Sankey uses nodes by column and height, and Sunburst uses depth-first pre-order. These model indexes are numbers.

Bar and Pie also support v-model:active-index, using positions within their own series. Sparkline, Tracker, Heatmap, CohortChart and CalendarHeatmap use their rendered item order. A controlled standalone root takes precedence over its Tooltip. A controlled chart-level Tooltip takes precedence over these series controls. Without a controlled Tooltip, each controlled series owns its active item and other series remain uncontrolled.

Multiple Tooltips share one selection and all receive change requests. The first registered Tooltip owns interaction settings (shared, trigger, axis-id, default-index, active); later Tooltips can set presentation options such as formatters, content, styles and cursors. Only one Tooltip in a chart may supply a controlled active-index. Conflicting bindings warn in development and the first controlled Tooltip wins.

An uncontrolled selection follows the same item through reorder and resize and clears when that item is removed or hidden. Controlled indexes address the current order. An invalid index shows no active target and requests null once for each distinct invalid input or target order.

For example, control a Tracker from another part of the page:

<Tracker v-model:active-index="active" :data="days" />

<button @click="active = null">
Clear selection
</button>

Hidden series

When hidden is bound, clicking a legend item shows or hides its series:

<script setup lang="ts">
const hidden = ref<string[]>([])
</script>

<template>
  <AreaChart
    :data="data"
    :height="300"
  >
    <Area data-key="desktop" />
    <Area data-key="mobile" />
    <Legend v-model:hidden="hidden" />
  </AreaChart>
</template>

Sunburst node indexes address the layout node list. Journey node and link indexes address their respective layout lists. Click events can carry a KeyboardEvent; hover events carry a MouseEvent.

Copyright © 2026