withExport puts a button in the chart's top corner. It opens a menu offering the
chart as a picture and as the numbers behind it:
Reach for it wherever the chart is the answer to a question somebody asked — a report they will send on, a dashboard tile whose numbers belong in a spreadsheet next. Leave it off where the chart is a small part of the page's furniture: a sparkline in a table cell, or a trend behind a headline number, is smaller than the button would be.
The button sits over the top corner of the plot rather than in a band of its own, so turning it on doesn't shorten the chart. That corner is the emptiest part of a plot — a value axis rounds up to a tick above its largest value — but it is not guaranteed empty, so check a chart whose data reaches the top right.
PNG and PDF are the chart as it is drawn, at the size it is being read at and at twice its pixel density. The PDF is one page, sized to the chart itself.
Both are rendered from a scene of their own rather than photographed off the page, which is what keeps them clean: no crosshair, no focus dot, no half-finished entrance animation, and the legend drawn into the picture rather than left behind as the buttons it is on screen.
CSV is the numbers, one row per category and one column per series:
Values go out at full precision rather than as the axis writes them — a tick reads
50k to fit the chart, and a cell that arrives in a spreadsheet that way is a
string nobody can sum. Dates are written YYYY-MM-DD HH:mm:ss, in the same frame
the category axis reads them in: UTC unless the axis asked for scale: 'time'.
A pie or donut is written as its slices, and a sankey as its flows, since neither has a category axis to lay a table out along.
exportFormats decides which files are offered, and in which order:
CSV drops out of the menu on its own for a chart with no rows to write: one whose
only marks are annotations, which label a plot rather than adding to it, and one
whose definition is a builder function, since a builder is free to answer with
marks Pluma never saw. A definition object doesn't take it away — that is laid
over the definition the marks produced, so the rows are still the chart's own.
A chart left with no formats at all draws no button, so exportFormats={['csv']}
on a chart Pluma has no rows for is the same as leaving withExport off.
Files are named after the chart's ariaLabel, as a slug — a chart labelled
"Messages delivered per month" downloads as
messages-delivered-per-month.png. Set exportFileName to name them yourself,
without an extension.
Nothing until it is used. The code that writes a file — the second renderer, the rasterizer, the PDF writer — is loaded when the reader picks a format, not when the chart renders, so a page full of downloadable charts carries none of it.
A chart is rasterized through the browser's own image decoder, which loads the chart's SVG as a document of its own. Web fonts don't reach it, so text in a PNG or a PDF is drawn in the nearest font the system has rather than the product's. Everything else — colors, type sizes, guide weights, and any CSS variable a consumer overrode — is measured off the chart on screen and written into the picture.
A series the reader has hidden from the legend is left out of every format, including the CSV. An export is what they are looking at, whichever form they asked for it in.