fix(core-components): draw non-interactive sparklines as SVG instead of an ECharts instance per cell - #3314
Open
timchambers wants to merge 1 commit into
Conversation
…ECharts instance per cell _Sparkline.svelte creates an offscreen ECharts instance for every non-interactive sparkline, purely to call renderToSVGString() — once in onMount and again in a reactive block on every update. DataTable renders one sparkline per row, so a table-heavy page creates hundreds of them. echarts.init() is roughly 30x slower in WebKit than in V8, so ~100 sparkline cells put Safari into a single ~24s uninterruptible main-thread task (32.6s on an iPhone). Chrome runs the same code fast enough to hide it. A non-interactive sparkline has no tooltip, no axis labels and no animation, so it does not need a chart engine. Add getSparklinePaths() — a dependency-free function that computes the same layout getSparklineConfig() asks ECharts for — and render it as an inline SVG. Interactive sparklines are untouched and still use ECharts. The non-interactive branch now emits identical markup on the server and in the browser, so hydration replaces like with like instead of discarding a prerendered ECharts SVG and rebuilding it.
timchambers
requested a deployment
to
Approval required to run action on external PR
August 9, 2026 17:16 — with
GitHub Actions
Waiting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
_Sparkline.sveltecreates an offscreen ECharts instance for every non-interactive sparkline, purely to callrenderToSVGString()— once inonMount, and again in a reactive block that re-runs on every update:DataTablerenders one sparkline per row (TableRow,GroupRow,SubtotalRowandTotalRowall passinteractive="false"), so a table-heavy page creates hundreds of chart instances.echarts.init()is roughly 30× slower in WebKit than in V8. On a page with ~100 sparkline cells that becomes a single ~24 s uninterruptible main-thread task in Safari — 32.6 s on an iPhone. Chrome runs the same code fast enough that nobody notices.A non-interactive sparkline has no tooltip, no axis labels and no animation. It doesn't need a chart engine.
The change
getSparklinePaths()tosparkline.js: a dependency-free pure function computing the same layoutgetSparklineConfig()asks ECharts for — xAxisboundaryGap: '2%', yAxisboundaryGap: ['1%','1%'],scale: yScale(a value axis withscale: falsealways includes zero),connectNulls: false, 1 px line, and the x axis line resting on zero whenever zero is in range._Sparkline.svelterenders that as an inline<svg>on the non-interactive path.connectGroupincluded.configis still computed in both cases, so the exportedconfigprop keeps its contract.One incidental improvement: the non-interactive branch now emits identical markup on the server and in the browser. Previously SSR prerendered an ECharts SVG and the client immediately discarded it to build another one; now hydration replaces like with like.
Measurements
Same machine, same build, served from a local static server. The control is a variant page holding all SQL blocks and rows constant (6 DataTables / 106 rows / 4 sparkline columns):
The cost is per instance, not per row of data: it scales with the number of sparkline cells rendered.
How this was isolated
Variant pages holding all 41 SQL blocks byte-identical while varying only the rendered components, macOS Safari, 3 repeats each (±0.3 s):
Deleting four
<Column contentType=sparkline>lines removes a 24-second block and makes the page indistinguishable from an empty one. Roughly 0.25 s per sparkline cell in WebKit.BigValueis not implicated — 19 BigValues with no DataTables behave like an empty page, even though several usesparkline=. Consistent with a per-instance cost: 4 instances ≈ 1 s (invisible), ~100 instances ≈ 25 s.Testing
sparkline.spec.js, 9 cases): axis inversion, zero anchoring vsyScale, null-driven line breaks, negative series lifting the baseline off the floor, bars hanging off both sides of it,nullfor nothing-renderable, single-point and flat-series degenerate cases, and date strings as well asDateobjects. All pass under the repo's vitest.@evidence-dev/core-components@5.4.2in a multi-tenant Evidence deployment, verified through a fullevidence buildincluding its hydration checks.pnpm run formatclean; changeset included.Notes
getSparklinePaths()is called per cell too — but it is arithmetic on ~24 points, not a chart engine.