Extending backends
July 24, 2026 · View on GitHub
Add a backend when Flint needs to target a new rendering library or spec format. A backend is an assemble<Backend>(input) orchestrator plus a templates/ registry; together they turn shared compiler output into a native chart spec or artifact. Existing references live under packages/flint-js/src/: vegalite/, echarts/, chartjs/, plotly/, and excel/.
For pipeline stages and repo layout, see Architecture.
Table of Contents
- §1 Create the skeleton
- §2 Follow the assembly contract
- §3 Add templates
- §4 Wire up the package
- §5 Site and gallery
- §6 Acceptance checklist
- §7 Related
§1 Create the skeleton
packages/flint-js/src/<backend>/
├── index.ts # public barrel
├── assemble.ts # orchestrator: ChartAssemblyInput → native spec
├── instantiate-spec.ts # encoding + layout → spec (optional; some backends inline this)
├── recommendation.ts # chart-type recommendations (optional)
└── templates/
├── index.ts # category map + getTemplateDef()
├── bar.ts
├── line.ts
└── …
Copy the closest existing backend before starting from scratch. Vega-Lite is the most complete reference for the shared pipeline; ECharts adds colormap.ts and facet.ts for backend-specific concerns.
§2 Follow the assembly contract
function assemble<Backend>(input: ChartAssemblyInput): <BackendSpec>
ChartAssemblyInput is defined in packages/flint-js/src/core/types.ts and includes data, chart_spec, semantic_types, options, and related fields.
Pipeline (do not skip core stages)
The orchestrator coordinates core/. It should not re-derive format, zero baseline, or color from raw field types.
PRE-PHASE normalizeStaticSeries(), applyEncodingOverrides()
(may need a preliminary resolveChannelSemantics for types)
PHASE 0 resolveChannelSemantics() → Record<channel, ChannelSemantics>
computeZeroDecision() per quantitative x/y (needs template mark)
chartProperties overrides (includeZero_*, logScale_*, …)
STEP 0a template.declareLayoutMode?.() → LayoutDeclaration
STEP 0b convertTemporalData()
STEP 0c computeChannelBudgets() + filterOverflow()
PHASE 1 computeLayout() → LayoutResult
PHASE 2 build backend encodings
template.instantiate(spec, InstantiateContext)
apply layout (vlApplyLayoutToSpec / ecApplyLayoutToSpec / …)
postProcess?, tooltips, facet combine
See packages/flint-js/src/vegalite/assemble.ts (file header + assembleVegaLite) for the canonical ordering.
IR boundary: downstream code reads flat ChannelSemantics and LayoutResult instead of re-inspecting semantic type strings.
§3 Add templates
Templates encode shape, not decisions. If a template needs to branch on field.type === 'temporal', move that logic to core/ instead.
Each template exports a ChartTemplateDef (core/types.ts):
| Field | Role |
|---|---|
chart | Display name — must match chart_spec.chartType |
template | Native spec skeleton (mark + encoding structure) |
channels | Allowed encoding slots |
markCognitiveChannel | position / length / area / color — drives zero baseline and compression |
declareLayoutMode? | Axis flags before layout (banded vs continuous, σ overrides) |
instantiate | Mutate spec from InstantiateContext (encodings, layout, semantics) |
properties? | Configurable chart properties |
postProcess? | Final visual tweaks after layout |
Register in templates/index.ts: import defs, add them to the category map, and expose *GetTemplateDef(chartType) as find(t => t.chart === chartType).
§4 Wire up the package
- Barrel —
export * from './<backend>'inpackages/flint-js/src/index.ts - Bundle — add a
packages/flint-js/tsup.config.tsentry (<backend>/index) - Exports — add the
"./<backend>"subpath inpackages/flint-js/package.json#exports - Smoke test — extend
packages/flint-js/tests/smoke.test.tswith oneassemble<Backend>()shape assertion - Gallery data — add
gen<Backend>*Tests()insrc/test-data/and register it inTEST_GENERATORS
§5 Site and gallery
- Gallery dev server:
npm run sitefrom the repo root, then open/gallery - Supported backends: update
site/src/shared/supported-backends.tsif the new backend should appear in the UI - Renderers: only add a new React view (
site/src/components/) when the spec format cannot reuseVegaLiteView,EChartsView, orChartjsView.TripleChartcurrently covers VL + ECharts + Chart.js.
Optional: wire the assembler into agent-skills/mcp-server/ if MCP clients should be able to call it.
§6 Acceptance checklist
A backend is ready when:
- Bar, line, area, and scatter templates render correctly on standard gallery matrices
-
tests/smoke.test.tspasses for the new assembler -
npm run typecheckandnpm run testpass at repo root - At least one dedicated test-data generator exercises backend-specific options
Parity note: not every chart name exists in every backend today. Document which templates you port; cross-backend parity is a goal, not a prerequisite for the first merge.
§7 Related
- Extending chart templates —
ChartTemplateDefauthoring - Auto Layout Algorithm — what
computeLayout()expects - API reference —
ChartAssemblyInputand assembler entry points