Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/clean-catalog-motion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/charts': minor
---

Improve definition-driven inference for optional motion renderers, raw chart specs with separate behaviors, responsive chart factories, and decorative marks.
98 changes: 95 additions & 3 deletions API-FRICTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ observed difficulty from examples, production migrations, tests, and agent
evaluations so later API, documentation, and TanStack Intent skill work is
based on evidence.

Last updated: 2026-08-12
Last updated: 2026-08-14

## Triage rule

Expand Down Expand Up @@ -323,6 +323,10 @@ Each entry records:
| F-284 | Stagger timing required repeated callback arithmetic | API | resolved |
| F-285 | Absolute catalog links lost their docs navigation tab | Documentation | resolved |
| F-286 | Browser imports treated raw JSON as a source module | Tooling | resolved |
| F-287 | Motion renderers required definition type extraction | API | resolved |
| F-288 | Generated examples exposed shared implementation scaffolding | Tooling/API | resolved |
| F-289 | Catalog workbenches exposed runtime bootstrap files | Tooling | resolved |
| F-290 | Public examples imported a private workspace package | Tooling | resolved |

## Findings

Expand Down Expand Up @@ -839,7 +843,7 @@ Each entry records:

- Status: resolved
- Severity: high
- Owner: Tooling
- Owner: Tooling/API
- Observed in: cross-library bundle and browser comparison matrix; executable
catalog production loading
- Friction: re-exporting one benchmark mount from a module containing four
Expand Down Expand Up @@ -904,7 +908,7 @@ Each entry records:

- Status: resolved
- Severity: medium
- Owner: Tooling
- Owner: Tooling/API
- Observed in: tiered cross-library benchmark validation
- Friction: `benchmark:check` and `benchmark:update-baseline` wrote their
size-only result to the canonical comparison paths. Running normal
Expand Down Expand Up @@ -8281,3 +8285,91 @@ Each entry records:
- Verification: the catalog contract validates all example imports, and the
revision-pinned esm.sh URL for the fixture returns a JavaScript module that
renders the production sandbox.

### F-287 — Motion renderers required definition type extraction

- Status: resolved
- Severity: medium
- Owner: API
- Observed in: making every catalog example show its authored chart before its
React shell
- Friction: `motion()` fixed its datum and axis generics when the renderer was
created, before `RendererChart` received the definition that already knew
those types. Seventy generated examples therefore extracted three conditional
types from the definition only to pass them back to `motion<...>()`.
- Decision: let the optional motion factory return a definition-agnostic
renderer whose generic methods acquire chart types from the host. Preserve
the explicit generic overload for low-level callers.
- Verification: the React type contract passes `motion()` directly beside a
typed definition, all 188 catalog entries typecheck, and no public example
contains a `motion<...>()` instantiation.

### F-288 — Generated examples exposed shared implementation scaffolding

- Status: resolved
- Severity: high
- Owner: Tooling
- Observed in: auditing the source shown for every catalog example
- Friction: generated ShadCN entries copied a complete shared stylesheet,
repeated definition type aliases and null component branches, and wrapped
the actual definition in another `defineChart()` call. Eighty-five older
catalog entries also exposed a generated `definition()` →
`createExampleChart()` chain. Seventy-nine public factories retained a
generic `ExampleOptions` bag containing dimensions, preview flags, and other
fields the authored definition never read. Merging tooltip behavior into the chart object
during cleanup made callback datum inference fall back to `unknown`.
Decorative guides also polluted the inferred interactive datum union even
though they cannot own focus or tooltip points. The newly exercised
`defineChart(responsiveFactory, behaviors)` path also exposed a runtime bug:
the constructor spread the factory like an object and silently dropped it.
- Decision: make each case-local `example.tsx` and stylesheet authoritative,
delete the 2,152-line shared ShadCN implementation and its source-extraction
script, and limit generation to metadata and conformance adapters. Collapse
every delegating definition factory into the public example factory, and
keep `defineChart(chart, behaviors)` as one call with two inference phases.
Raw static and responsive overloads infer the chart before behaviors, while
`decorative()` contributes scale types but no interactive datum type.
Preserve a responsive factory as the definition's `chart` callback when
separate behaviors are supplied.
Omit unused component branches, empty spreads, unused option fields, and
unused CSS. Name the remaining definition-driving input `ChartOptions`. Keep
conformance adapters and Recharts references outside the public import
closure; interaction tests import the real examples directly.
- Verification: generation produces 70 entries whose public factory contains
the authored definition, the catalog contract rejects nested or locally
delegated definitions and validates 188 self-contained entries with a
largest TypeScript closure of four files. The cleanup is idempotent after a
fresh 70-case ShadCN metadata generation. A catalog-wide cleanup reduces all
79 legacy option bags to the properties their definitions actually read and
rejects the old `ExampleOptions` surface. The complete workspace typecheck, 1,877
unit tests, packed-package checks, 188 generated preview checks, and 70/70
ShadCN visual comparisons pass.

### F-289 — Catalog workbenches exposed runtime bootstrap files

- Status: resolved
- Severity: medium
- Owner: Tooling
- Observed in: opening Charts examples on tanstack.com
- Friction: `/__catalog.tsx` and `/index.html` are generated execution plumbing,
but the workbench displayed both beside authored source. They were also
retained as visible files after sharing an example.
- Decision: mark generated bootstrap paths as hidden example metadata. Keep
them in the executable workspace while filtering them from initial-file
selection, tabs, the file explorer, and shared-project views.
- Verification: tanstack.com catalog and shared-project contract tests preserve
the hidden paths while confirming both bootstrap files remain runnable.

### F-290 — Public examples imported a private workspace package

- Status: resolved
- Severity: medium
- Owner: Tooling
- Observed in: opening all 188 catalog examples in the public workbench
- Friction: 163 entries displayed `@charts-poc/demo-data`, exposing an internal
package name and requiring the host to understand a private convention.
- Decision: expose revision-pinned fixture subpaths through the stable
`@tanstack/charts-data` catalog alias and reject private package imports from
every public source closure.
- Verification: the catalog contract resolves every fixture subpath as a
browser module and reports no `@charts-poc/` import in any public example.
2 changes: 1 addition & 1 deletion benchmarks/bundle-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ of the default scene and host contracts. Default static SVG consumes scene
clips and gradients. Their reviewed shared-path cost is recorded in the locked
entries and the corresponding complete-consumer budgets. Rolling path planning
remains confined to the opt-in motion renderer, whose complete SVG budget is
18.2 KiB gzip.
20.75 KiB gzip.

Every public transform family has an isolated budget and retained-input
allowlist. Numeric and 2D bins may retain `d3-array`, and row stacks may retain
Expand Down
32 changes: 16 additions & 16 deletions benchmarks/bundle-size/universal-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
"policy": "Exact minified and gzip output for entries that optional features must not affect. Review every change before updating.",
"bundles": {
"D3-scale line scene": {
"bytes": 47115,
"gzip": 17898
"bytes": 47151,
"gzip": 17902
},
"D3-scale line + static SVG": {
"bytes": 51848,
"gzip": 19616
"bytes": 51884,
"gzip": 19619
},
"Representative marks": {
"bytes": 71536,
"gzip": 26220
"bytes": 71572,
"gzip": 26228
},
"TanStack DOM host": {
"bytes": 70878,
Expand All @@ -23,24 +23,24 @@
"gzip": 25621
},
"React line consumer": {
"bytes": 96231,
"gzip": 34868
"bytes": 96267,
"gzip": 34866
},
"Compact-scale line scene": {
"bytes": 29572,
"gzip": 10769
"bytes": 29608,
"gzip": 10773
},
"React compact-scale line consumer": {
"bytes": 78732,
"gzip": 27803
"bytes": 78768,
"gzip": 27799
},
"Custom-scale line scene": {
"bytes": 27756,
"gzip": 10032
"bytes": 27792,
"gzip": 10035
},
"D3 linear-scale line scene": {
"bytes": 47047,
"gzip": 17863
"bytes": 47083,
"gzip": 17866
}
}
}
128 changes: 64 additions & 64 deletions benchmarks/comparison/bundle-baseline.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"schemaVersion": 4,
"generatedAt": "2026-08-14T05:55:21.257Z",
"generatedAt": "2026-08-15T06:11:41.238Z",
"packageVersions": {
"tanstack": "0.12.0",
"tanstack": "0.13.0",
"chartjs": "4.5.1",
"echarts": "6.1.0",
"recharts": "3.10.1",
Expand All @@ -11,8 +11,8 @@
"sources": {
"tanstack": {
"kind": "workspace",
"revision": "56567ec51902d8af0d0d730f3a31206999cc8438",
"inputDigest": "sha256:6755913ae5f9c34a2bbb1ccb7cd2144274bc37d74051c091fc279ac3d1d7a4cd"
"revision": "91e2eef1772dc06118ce5aa1e0b388d12bf91918",
"inputDigest": "sha256:d3480809cd111f9acf9bbaf58be2b4f42bc56c4a748fcc151d49025a2c444e88"
},
"chartjs": {
"kind": "package",
Expand Down Expand Up @@ -45,88 +45,88 @@
},
"bundles": {
"tanstack-line-basic": {
"minifiedBytes": 105944,
"gzipBytes": 38533,
"brotliBytes": 34109,
"incrementalGzipBytes": 38533,
"incrementalBrotliBytes": 34109
"minifiedBytes": 105980,
"gzipBytes": 38537,
"brotliBytes": 34114,
"incrementalGzipBytes": 38537,
"incrementalBrotliBytes": 34114
},
"tanstack-line-interactive": {
"minifiedBytes": 111385,
"gzipBytes": 40302,
"brotliBytes": 35551,
"incrementalGzipBytes": 40302,
"incrementalBrotliBytes": 35551
"minifiedBytes": 111421,
"gzipBytes": 40306,
"brotliBytes": 35539,
"incrementalGzipBytes": 40306,
"incrementalBrotliBytes": 35539
},
"tanstack-line-advanced": {
"minifiedBytes": 118575,
"gzipBytes": 42629,
"brotliBytes": 37541,
"incrementalGzipBytes": 42629,
"incrementalBrotliBytes": 37541
"minifiedBytes": 118611,
"gzipBytes": 42632,
"brotliBytes": 37568,
"incrementalGzipBytes": 42632,
"incrementalBrotliBytes": 37568
},
"tanstack-bar-basic": {
"minifiedBytes": 114612,
"gzipBytes": 41793,
"brotliBytes": 36837,
"incrementalGzipBytes": 41793,
"incrementalBrotliBytes": 36837
"minifiedBytes": 114648,
"gzipBytes": 41796,
"brotliBytes": 36865,
"incrementalGzipBytes": 41796,
"incrementalBrotliBytes": 36865
},
"tanstack-bar-interactive": {
"minifiedBytes": 118904,
"gzipBytes": 43127,
"brotliBytes": 37954,
"incrementalGzipBytes": 43127,
"incrementalBrotliBytes": 37954
"minifiedBytes": 118940,
"gzipBytes": 43130,
"brotliBytes": 37969,
"incrementalGzipBytes": 43130,
"incrementalBrotliBytes": 37969
},
"tanstack-bar-advanced": {
"minifiedBytes": 119243,
"gzipBytes": 43268,
"brotliBytes": 38025,
"incrementalGzipBytes": 43268,
"incrementalBrotliBytes": 38025
"minifiedBytes": 119279,
"gzipBytes": 43273,
"brotliBytes": 38033,
"incrementalGzipBytes": 43273,
"incrementalBrotliBytes": 38033
},
"tanstack-area-basic": {
"minifiedBytes": 111028,
"gzipBytes": 40457,
"brotliBytes": 35802,
"incrementalGzipBytes": 40457,
"incrementalBrotliBytes": 35802
"minifiedBytes": 111064,
"gzipBytes": 40462,
"brotliBytes": 35868,
"incrementalGzipBytes": 40462,
"incrementalBrotliBytes": 35868
},
"tanstack-area-interactive": {
"minifiedBytes": 116469,
"gzipBytes": 42189,
"brotliBytes": 37272,
"incrementalGzipBytes": 42189,
"incrementalBrotliBytes": 37272
"minifiedBytes": 116505,
"gzipBytes": 42194,
"brotliBytes": 37277,
"incrementalGzipBytes": 42194,
"incrementalBrotliBytes": 37277
},
"tanstack-area-advanced": {
"minifiedBytes": 123841,
"gzipBytes": 44639,
"brotliBytes": 39311,
"incrementalGzipBytes": 44639,
"incrementalBrotliBytes": 39311
"minifiedBytes": 123877,
"gzipBytes": 44638,
"brotliBytes": 39304,
"incrementalGzipBytes": 44638,
"incrementalBrotliBytes": 39304
},
"tanstack-scatter-basic": {
"minifiedBytes": 106983,
"gzipBytes": 38919,
"brotliBytes": 34369,
"incrementalGzipBytes": 38919,
"incrementalBrotliBytes": 34369
"minifiedBytes": 107019,
"gzipBytes": 38925,
"brotliBytes": 34417,
"incrementalGzipBytes": 38925,
"incrementalBrotliBytes": 34417
},
"tanstack-scatter-interactive": {
"minifiedBytes": 112424,
"gzipBytes": 40678,
"brotliBytes": 35859,
"incrementalGzipBytes": 40678,
"incrementalBrotliBytes": 35859
"minifiedBytes": 112460,
"gzipBytes": 40681,
"brotliBytes": 35819,
"incrementalGzipBytes": 40681,
"incrementalBrotliBytes": 35819
},
"tanstack-scatter-advanced": {
"minifiedBytes": 112440,
"gzipBytes": 40683,
"brotliBytes": 35866,
"incrementalGzipBytes": 40683,
"incrementalBrotliBytes": 35866
"minifiedBytes": 112476,
"gzipBytes": 40686,
"brotliBytes": 35826,
"incrementalGzipBytes": 40686,
"incrementalBrotliBytes": 35826
},
"chartjs-line-basic": {
"minifiedBytes": 137909,
Expand Down
Loading