
Skill
coordinate-charts-with-tanstack
coordinate TanStack Charts with data sources
Description
Coordinate TanStack Charts with TanStack Table data grids, TanStack DB live queries and sync engines, TanStack Query server state, Store, Router, Virtual, Pacer, Start, and Form without duplicating ownership. Load for chart-grid linking, shared filtering or selection, live synchronized data, optimistic updates, URL-restorable chart state, virtualized detail views, paced streams, or dashboard-wide state.
SKILL.md
Coordinate Charts With TanStack
Run trigger → inspect → decide → build → verify across the complete product state graph. Assign one owner to each row projection, semantic key, filter, selection, viewport, revision, transaction, and timing policy before connecting libraries.
Setup
Inspect the ownership graph
Record these boundaries before writing adapters:
| Concern | Normal owner | Chart input |
|---|---|---|
| Remote fetching, cache, freshness, retry | Query | resolved semantic snapshot plus status |
| Normalized records, live queries, optimistic transactions | DB or sync collection | keyed projection plus sync state |
| Grid filtering, sorting, grouping, pagination | Table when grid-local; application when shared | explicitly chosen row-model frontier |
| Visual encoding, focus, scene, renderer | Charts | prepared rows and committed behavior options |
| Shared client intent | Store or application state | semantic filters, keys, and domains |
| Restorable navigation state | Router search | validated compact intent, never pixels or row objects |
| Mounted grid window | Virtual | no analytical effect unless “visible rows” is the stated question |
| Burst, queue, or batch policy | Pacer | paced source revisions or committed actions |
| Draft controls and validation | Form or local state | committed, valid intent |
| SSR request and hydration | Start or framework lifecycle | deterministic data revision, locale, and initial geometry |
Fail condition: two libraries can independently change the same semantic state, or the chart receives rows without a named scope and revision.
Name each row projection
Do not pass “the rows” between surfaces. Name the analytical frontier:
sourceRows: canonical records available to this client.eligibleRows: records after shared business filters.summaryRows: chart grain after aggregation or binning.detailRows: grid grain before presentation-only sorting or pagination.visibleRows: currently mounted virtual items; usually presentation only.selectedRows: rows resolved from stable semantic keys.
Document whether grid sorting, filtering, grouping, pagination, or selection changes the chart question. Use the Table and data-grid reference for row-model choices.
Core Patterns
Derive once, project deliberately
Use one semantic projection owner, then expose separate summary and detail outputs:
const analysis = projectOrders(sourceRows, sharedIntent)
const tableData = analysis.detailRows
const chartDefinition = defineChart({
marks: [
lineY(analysis.summaryRows, {
x: 'day',
y: 'revenue',
color: 'region',
key: ({ datum }) => `${datum.region}:${datum.day.toISOString()}`,
}),
],
x: { scale: scaleUtc },
y: { scale: scaleLinear },
})
Do not independently restate eligibility, aggregation, or metric formulas in Table accessors and chart transforms. Presentation-only table sort and pagination may remain grid-owned.
Synchronize semantic intent, not component internals
Share values such as:
- entity IDs or aggregate bucket keys;
- validated filters and comparison periods;
- committed scale domains or time windows;
- selected metric and grouping dimension;
- data revision and “as of” status.
Keep DOM nodes, row indexes, chart pixels, virtual indexes, tooltip bodies, and Table row objects inside their owning surface.
Separate transient, committed, and persisted state
Use three phases where interaction can write data:
- Transient: hover, pointer position, drag preview, and active handle remain local and cheap.
- Committed: selection, accepted brush domain, and submitted filters update shared application or URL state once.
- Persisted: DB or server writes expose pending, confirmed, failed, and rollback states explicitly.
Do not write every pointer frame to Router, Query, a sync engine, or a database transaction. Read Query, DB, and sync when records are live or writable.
Pace at the expensive boundary
Choose timing by semantics:
- debounce draft search or filters that should wait for intent;
- throttle disposable visual previews that need bounded frequency;
- queue writes when every operation must persist;
- batch compatible mutations, telemetry, or source revisions;
- preserve latest-wins correctness for chart presentation even when writes are lossless.
Do not add a second scheduler around work already bounded by the chart host, live-query engine, or framework.
Verify the full synchronization sequence
- Load one named data revision into both surfaces.
- Apply a shared filter and prove summary totals reconcile with detail rows.
- Sort and paginate the grid; prove the chart changes only when intended.
- Select from chart and grid; prove both resolve the same semantic keys.
- Apply an optimistic update; distinguish pending from confirmed output.
- Roll back or receive a remote update; preserve surviving selection and clear missing keys.
- Restore URL state, hydrate, resize, virtualize, and destroy without duplicate subscriptions or writes.
Common Mistakes
CRITICAL Charting the final rendered row model accidentally
Wrong: feed the chart whatever rows the grid currently renders.
Correct: choose the filtered, grouped, pre-pagination, page, or selected frontier because it matches the stated comparison.
Table row models can include sorting, grouping, expansion, and pagination. The final grid row model is a presentation result, not an automatically valid analytical dataset.
Source: TanStack Table row-model guide
CRITICAL Keying coordination by row or virtual index
Wrong: synchronize row.index, array position, or virtual item index.
Correct: define an application-level entity or aggregate key and adapt both Table and Charts to it.
Indexes change under sorting, filtering, pagination, streaming, and virtualization. Grouped Table row IDs may also contain presentation suffixes.
Source: TanStack Table rows guide; API-FRICTION.md F-120, F-131, and F-239
CRITICAL Duplicating filtering and aggregation
Wrong: implement the KPI in a Table aggregation function and again in chart preparation.
Correct: derive one named semantic projection that can feed the grid, chart, table alternative, export, and tests.
Independent pipelines drift on null policy, denominators, time boundaries, and later product changes.
Source: docs/guides/transforms-and-reactivity.md; API-FRICTION.md F-128 and F-163
CRITICAL Creating a bidirectional state loop
Wrong: chart selection sets a Table filter whose change recreates chart selection and writes the URL again.
Correct: assign one owner, normalize one event into semantic intent, and let every surface derive from that state without echoing equivalent updates.
Feedback loops cause redundant renders, history spam, flicker, and state that cannot settle.
Source: Charts interaction ownership; TanStack Router search-state guidance
HIGH Treating optimistic rows as confirmed history
Wrong: animate an optimistic mutation into the ordinary historical series with no pending state.
Correct: carry sync or transaction status into the semantic projection, differentiate pending values when decision-relevant, and verify rollback.
TanStack DB applies optimistic state before persistence and can replace or roll it back after synchronization.
Source: TanStack DB mutations and live-query guides
CRITICAL Persisting every interaction frame
Wrong: write every brush, handle, resize, or pointer update through Router, Query invalidation, or a sync transaction.
Correct: preview locally, pace expensive derivation if necessary, and commit one semantic value at the interaction boundary.
Frame-rate state is presentation. Persistence needs validation, cancellation, ordering, and conflict semantics.
Source: Charts interaction guides; TanStack Pacer timing guidance
HIGH Treating virtualization as analytical filtering
Wrong: chart only mounted grid rows because Virtual currently exposes them.
Correct: chart the intended source or filtered projection; use mounted rows only for an explicitly viewport-scoped question.
Virtual controls render work, not dataset meaning.
Source: TanStack Virtual virtualizer reference
References
See also: prepare-chart-data/SKILL.md, build-chart-interactions/SKILL.md, update-and-animate-charts/SKILL.md, and debug-and-verify-charts/SKILL.md.
More skills from the charts repository
View all 12 skillsbuild-chart-interactions
build chart interactions with TanStack Charts
Aug 15ChartsFrontendInteractionTanStackcompose-marks-and-views
compose marks and views in TanStack Charts
Aug 15ChartsFrontendTanStackUI Componentsconfigure-scales-guides-color
configure scales and guides for TanStack Charts
Aug 15ChartsData VisualizationFrontendTanStackdebug-and-verify-charts
debug and verify TanStack Charts
Aug 15ChartsDebuggingFrontendTanStackdesign-a-chart
design charts with TanStack Charts
Aug 15ChartsData VisualizationFrontendTanStackdesign-responsive-charts
design responsive TanStack Charts
Aug 15ChartsData VisualizationFrontendSSR +1
More from TanStack
View publisheraggregation
perform data aggregation in TanStack Table
table
Aug 11Data AnalysisFrontendTanStackapi-not-found
diagnose TanStack Table API errors
table
Aug 11DebuggingFrontendTanStackcell-selection
select rectangular cell ranges in tables
table
Aug 11Data AnalysisTanStackUI Componentscell-spanning
configure cell spanning in TanStack Table
table
Aug 11Data VisualizationFrontendTanStackUI Componentsclient-vs-server
manage TanStack Table data pipelines
table
Aug 11Data PipelineFrontendPerformanceTanStackcolumn-faceting
build faceted filter UIs
table
Aug 11Data VisualizationFrontendTanStack