LayerCake: a headless graphics framework for Svelte charts
graphics framework for sveltejs
At a glance
- What is it?
- A Svelte library that provides the empty layer components, the scales and the plumbing for a chart, and leaves the actual chart to you, with SVG, Canvas and HTML layers side by side in one tree. Version 11 replaced the Svelte store API with runes, so upgrading is a rewrite of the examples rather than a version bump.
- Who is it for?
- LayerCake suits a Svelte team that wants to own its chart rendering, for example to draw hundreds of marks on a canvas, to mix an SVG axis with a canvas data layer, or to share one scale across several linked charts, and that is prepared to write the chart components rather than configure a library. It does not suit a team that wants a finished chart component to drop in, and it does not suit a project staying on the Svelte store API, because version 11 moved to runes.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Svelte, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Headless, and what that means in practice
LayerCake is described as a headless visualization framework for mostly-reusable graphics with Svelte. That word, headless, is the whole design in one term.
The library provides a main wrapper component and a set of empty layout components. You fill them with your own chart components, which live inside your project and which you can copy and paste from the library's examples as starting points. The comment in the README's example says exactly that.
The layer components you get are `Svg`, `Html` and `Canvas`. That trio is the reason to use this library rather than a charting library. A single chart can have its axis in SVG, its data marks in a canvas, and its labels in HTML, all inside one tree, all sharing one coordinate system. SVG gives you crisp text and easy hit testing, canvas gives you thousands of marks without DOM overhead, and HTML gives you real text that can be selected and styled with CSS.
The wrapper takes the data accessors as props, so `x='x'` and `y='y'` say which field of each data object is the x and the y value, and the `data` prop is the array itself. The layers then work in that abstract space and the components you write consume the same scale.
The project is MIT licensed, the primary language is Svelte, the default branch is main, and the last push was on 2026-10-04. The releases are v11.0.0 from 2026-09-06, v10.0.0 from 2025-08-19 and v9.0.0 from 2025-08-01, and the release names are playful subtitles, the v11 one being the No More Stores Edition.
The package is on npm as `layercake` and the site at layercake.graphics carries the examples, the guide, the API documentation, a component gallery and a starter template.
The v11 runes change is a rewrite, not a bump
The first thing the README tells you is which Svelte version the library targets: since v11, the library and the examples use runes instead of the Svelte store API. The v10 documentation is archived for the examples of those older components.
The implication is significant for anyone with an existing chart. The store API was how a Svelte component published a value that descendants could read, and a chart library built on it would have had to reach into stores to expose scales and dimensions to your components. Runes replace that with a different reactivity model, and a component that consumed a store has to be rewritten.
For the library's own examples, the archive is a migration reference: you can see the v10 version of every component and the v11 version side by side.
For a user, the practical question is timing. The v10 release was in August 2025 and v11 in September 2026, so the store-based line is a year old and the runes line is new. A project that adopts v11 starts on the current API, and a project with existing LayerCake components on v10 has a migration to plan, component by component.
This is also why the project publishes a starter template as a separate repository, `layercake-template`. A template that already uses the current reactivity model is the fastest way for someone to find out whether the library fits before they port anything.
The example, and what it actually tells you
The README's example is the whole API in one file, and it is worth reading line by line rather than skimming.
<script>
// The library provides a main wrapper component
// and a bunch empty layout components...
import { LayerCake, Svg, Html, Canvas } from 'layercake';
// ...that you fill with your own chart components,
// that live inside your project and which you
// can copy and paste from here as starting points.
import AxisX from './components/AxisX.svelte';
import AxisY from './components/AxisY.svelte';
import Line from './components/Line.svelte';
import Scatter from './components/Scatter.svelte';
import Labels from './components/Labels.svelte';
const data = [
{ x: 0, y: 1 },
{ x: 1, y: 2 },
{ x: 2, y: 3 }
];
</script>Three things stand out. Your own components, the axis, the line, the scatter and the labels, are imported from the project's own `components/` directory, not from the package. The data is an array of plain objects with `x` and `y` fields, which is the shape a database or an API returns. And the wrapper is configured with the accessors as string props.
The template part shows the same three layers holding three different components:
<div class="chart-container">
<LayerCake
x='x'
y='y'
{data}
>
<Svg>
<AxisX/>
<AxisY/>
<Line stroke='#f0c'/>
</Svg>
<Canvas>
<Scatter fill='#0fc'/>
</Canvas>
<Html>
<Labels/>
</Html>
</LayerCake>
</div>The axes are SVG because they need text and strokes, the scatter is on canvas because there may be many points, and the labels are HTML because they are text that should be selectable. The `stroke` and `fill` attributes are passed straight through to the components you wrote, which is the convention: the components are yours, so their props are yours too, and the library only decides the layer they live in and the coordinate space they share.
The container carries a width of 100 percent and a height of 500 pixels, and the size of the container is what the chart fills.
The tooling: SvelteKit, Playwright, Mocha and a dimension doc generator
The `package.json` describes a project that treats its own documentation as a build artefact.
The site is a SvelteKit application with the static adapter, and the library is published with `svelte-kit sync && svelte-package -o dist && publint`, so the packaging step runs the library's own tooling and then checks the result with publint before it goes out. The `prepackage` hook runs `pnpm generate:dims` first.
That generator is the interesting one. `generate:dims` runs `node src/scripts/generateDimensionDocs.js`, and the name says what it does. The library has a set of chart dimensions, the size of the plotting area, the padding, the margins, and the documentation for them is generated from the source rather than maintained by hand. For a library whose whole value is that its parts are reusable and composable, a single source of truth for the dimension names and their documentation is a reasonable thing to automate, and it removes a class of drift.
The test setup has two layers with two different runners. `test:lib` runs Mocha over `test/lib/*.test.js` for the library logic, and `test:playwright` runs Playwright for the rendered output, with a `pretest:playwright` hook that installs the Chromium browser first so the suite is reproducible on a fresh machine. There is a separate `test:init:snapshots` script for regenerating the Playwright snapshots, which is the standard way to review a visual change rather than to accept it silently.
The remaining scripts are conventional: `dev` and `preview` run Vite, `build` builds with Vite, `check` runs `svelte-check` against the `jsconfig.json`, and `lint` and `format` are Prettier. The package manager is pinned to pnpm 10.34.5, and the repository has a `pnpm-lock.yaml`, a `vite.config.js`, a `playwright.config.js` and a `DEVELOPMENT.md`.
The d3 dependency, and what it tells you about the scope
The dev dependencies list a large set of d3 packages, and reading them tells you what the examples and the guide cover.
There is `d3-scale`, `d3-shape`, `d3-array` style building blocks, plus `d3-geo` and `@types/geojson` for maps, `d3-hierarchy` for tree layouts and `d3-quadtree` for spatial indexing, `d3-sankey` for flow diagrams, `d3-force` for force layouts, `d3-delaunay` for Voronoi and triangulation work, `d3-dsv` and `@rollup/plugin-dsv` for delimited data loading, and `d3-time` with `d3-time-format` for temporal axes.
What that list means is that LayerCake is not trying to reimplement the parts of d3 that do layout and geometry. It is trying to provide the part d3 does not do, which is the reactive glue between a Svelte component tree and a set of scales, and the layer containers that decide whether a mark is drawn as SVG, as canvas or as HTML.
The d3 packages are dev dependencies rather than runtime dependencies, which is the important detail. The published library carries the layout and the scale plumbing and the layer components, and the adopter brings the d3 modules their own chart needs. A project that draws only lines does not ship the sankey layout.
Two entries stand apart: `golden-fleece` and `emoji-regex`. Neither is a charting library, and their presence says the project cares about the performance of parsing and about handling text that contains emoji, which is a real issue for axis labels.
So the shape of the dependency list is the shape of the project: a small reactive core with a large set of optional recipes built on the wider d3 ecosystem.
Where LayerCake is the wrong choice
The first wrong case is a project that wants a chart component to drop in. LayerCake gives you layers and scales and expects you to write the axis, the line, the scatter and the labels. That is a real amount of work, and if you wanted a component with a config object, you have taken on a project rather than a dependency.
The second is a team that has not chosen to write chart components at all. A lot of dashboard work is done better with a library that ships the chart, and the effort LayerCake saves you is effort you have to spend instead.
The third is a project that is not on Svelte, or on a Svelte version that has not moved to runes. The v11 change means the current library targets the runes model, and a project on the store API has to port its components. That is a per-component cost, and the archived v10 documentation is the reference for doing it.
The fourth is a project with a hard requirement on bundle size. A chart library that ships nothing by default is a good fit, but the moment you add d3-geo for a map you are shipping a map library, and the adopter is making those choices one module at a time.
The fifth is a team that wants server-side rendered charts with no JavaScript at all. This is a Svelte component library, so the charts are components rather than static markup.
What LayerCake is good at is the case where the chart is the product. When a dashboard has a chart nobody has built before, and the team needs it to match the design system exactly, and it needs two charts linked by a shared scale, writing the components is the correct move and this library is the shortest path to doing it.
Editorial conclusion
LayerCake suits a Svelte team that wants to own its chart rendering, for example to draw hundreds of marks on a canvas, to mix an SVG axis with a canvas data layer, or to share one scale across several linked charts, and that is prepared to write the chart components rather than configure a library. It does not suit a team that wants a finished chart component to drop in, and it does not suit a project staying on the Svelte store API, because version 11 moved to runes. Before adopting it, read the v11 change and the archived v10 documentation, and try the starter template at `layercake-template` first, since the library's value is in its examples rather than in a configuration file.
Frequently asked questions
How do I install LayerCake?
With `npm install --save layercake`. The package is published on npm as `layercake`, and the site at layercake.graphics carries the examples, the guide, the API documentation and a component gallery.
What changed in LayerCake v11?
Since v11 the library and the examples use runes instead of the Svelte store API. The v10 documentation is archived with the examples of those older components, which serves as the migration reference for existing charts.
What are the Svg, Canvas and Html components in LayerCake?
They are the empty layer containers the library provides. You place your own chart components inside them, so an axis can be SVG for crisp text, a scatter can be Canvas for many marks, and labels can be HTML for selectable text, all sharing one coordinate space.
How are LayerCake charts tested?
The library logic is tested with Mocha over `test/lib`, and the rendered output is tested with Playwright, whose Chromium browser is installed by a pretest hook. There is a separate script to regenerate the Playwright snapshots.
Does LayerCake include the d3 charting modules?
No, the d3 packages are dev dependencies, so they are used by the examples and the guide rather than shipped with the library. A project brings the d3 modules its own charts need, such as d3-scale, d3-geo, d3-hierarchy or d3-sankey.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mhkeller-layercake)Was this useful?