TanStack Charts owns datum focus and selection. The application owns gestures that change a domain, range, viewport, or product record.
This boundary keeps the default host small and lets applications use battle-tested interaction controllers only when needed.
Use native chart focus for:
Use controlled application state for:
See Tooltips and Focus for the native path.
Every application-owned gesture follows the same loop:
Do not mutate SVG geometry directly and then attempt to reconcile application state afterward.
Copy the same configured D3 scale onto the resolved plot range:
const interactionX = xScale
.copy()
.range([scene.chart.x, scene.chart.x + scene.chart.width])
const date = interactionX.invert(pointerX)For y, reverse the range:
const interactionY = yScale
.copy()
.range([scene.chart.y + scene.chart.height, scene.chart.y])Apply UTC month snapping, numeric rounding, minimum ranges, and domain clamps after inversion. Those policies are application semantics, not scale math.
The Scales and D3 page is the sole source for D3 ownership and official interaction-module links.
When a gesture owns the chart surface, disable native focus explicitly:
import { focusDisabled } from '@tanstack/charts/focus/disabled'
const gestureDefinition = defineChart(definition, {
focus: focusDisabled,
keyboard: false,
})
mountChart(element, {
definition: gestureDefinition,
ariaLabel: 'Selectable monthly range',
onRender: mountBrushOverlay,
})This prevents a brush or free cursor from competing with the host's point marker and tooltip. It does not remove keyboard accessibility from the application-owned controls.
A complete brush owns:
Use d3-brush and d3-selection as optional direct dependencies when their controller semantics fit. The chart library does not bundle them.
Zoom state should be a semantic domain, not an opaque transform trapped in a DOM behavior. Decide:
Use d3-zoom and d3-selection when they improve modality handling. Feed the resulting domain back into the chart input.
Store one semantic cursor, selection, or domain and derive every view from it. Each view can have an independent y scale while sharing a date or category.
Validate outgoing events by semantic value, not matching pixel positions. Different chart sizes and margins should still resolve the same selection.
Scrubbers and editable ranges should pair direct manipulation with native controls:
The chart renders the controlled state. It does not become the form control.
For resource timelines, native horizontal overflow is often better than capturing the wheel:
An interaction controller may install pointer capture, event listeners, observers, nested chart hosts, or animation frames. onRender can update an existing controller, but the application surface must destroy every resource when the chart unmounts or ownership changes.