Components

Brush

Select a window of rows in a cartesian chart

<Brush> adds a range selector below a cartesian chart. The chart shows only the rows inside the selected window. Drag a traveller to resize the window, or drag the window to move it.

Brush with a panorama

Put a small chart in the Brush's default slot to show the whole series behind the window. Drag the handles: the text below the chart follows v-model:range.

The panorama chart gets the parent's data, size and position from the Brush, so it needs only its series. Put exactly one chart in the slot (AreaChart, LineChart or BarChart); the Brush ignores the slot otherwise.

Control the range

<script setup lang="ts">
import { ref } from 'vue'
import type { BrushStartEndIndex } from 'vccs'
import { Bar, BarChart, Brush, XAxis } from 'vccs'

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

<template>
  <BarChart
    :data="data"
    :height="300"
  >
    <XAxis data-key="name" />
    <Bar data-key="value" />
    <Brush
      v-model:range="range"
      data-key="name"
    />
  </BarChart>
</template>

The range is a pair of row indexes, both included. null selects all rows and hides the travellers.

  • Without range, the Brush owns its window. It starts with all rows.
  • With range (including null), you own the window. The Brush asks for changes with update:range; the window changes only when you accept the value.
  • The Brush rounds indexes down, clamps them to the data, and puts them in order. Values that are not finite become null. When it corrects a value you supplied, it emits update:range once with the corrected value.

When the data changes

An uncontrolled Brush keeps its window by row index:

  • Same number of rows: the window does not change.
  • Rows added, window ends at the last row: the window moves forward and keeps its width, so it follows the newest rows. A window over all rows stays over all rows.
  • Rows added, window elsewhere: the window keeps its positions.
  • Rows removed: the window is clamped to the new length. It resets only when no rows remain.

A moved or clamped window emits update:range. A controlled v-model:range is never changed by the Brush; it only asks.

When the data becomes empty, the Brush emits null. If you accepted that null, the Brush asks for all rows when rows come back, so a filter that briefly finds nothing does not remove the travellers.

This rule differs from Recharts, which resets the window when it gets a new data array.

Props

PropTypeDefaultDescription
rangeBrushStartEndIndex | null—Selected rows { startIndex, endIndex }. Use with v-model:range
data-keystring | (row) => unknown—Field shown in the traveller text. Without it, the text shows the row index
tick-formatter(value, index) => string—Formats the traveller text
heightnumber40Height of the brush in px
widthnumberplot widthWidth of the brush in px
xnumberplot leftLeft edge in px
ynumberbelow the plotTop edge in px
dynumber—Extra vertical offset in px
traveller-widthnumber5Width of each handle in px
gapnumber1Index step for dragging. With 3, the window moves 3 rows at a time
fillstringvar(--v-charts-background, #fff)Background fill
strokestringvar(--v-charts-axis, #666)Border, window and handle color
padding{ top, right, bottom, left }1 on each sideMargin of the panorama chart inside the brush
always-show-textbooleanfalseShow the traveller text all the time, not only while you drag, hover or focus
leave-time-outnumber1000Milliseconds after the pointer leaves the chart before a drag ends
classstring | array | object—Class on the brush group

Other attributes, such as aria-label and data-*, go to the brush group ([data-slot="brush"]).

Events

EventPayloadWhen
update:rangeBrushStartEndIndex | nullThe Brush asks for a new window: drag, keyboard, a correction of your value, or data changes
changeBrushStartEndIndexThe window changed through a drag or the keyboard
drag-endBrushStartEndIndexA drag of the window or a handle ended

Slots

SlotDescription
defaultOne chart drawn inside the brush as a panorama. It receives data, x, y, width, height and margin from the Brush

Keyboard

Each handle is a slider (role="slider") in the tab order, labelled "Range start" and "Range end". ArrowLeft and ArrowRight move the focused handle by one row. The slider reports the row index as aria-valuenow and the category text as aria-valuetext. A null range hides the handles.

Synchronised charts

Charts with the same sync-id share the Brush window. A chart without its own Brush follows the window of a synchronised chart that has one. See Events and v-model for the model rules and Accessibility for keyboard support in other components.

Copyright © 2026