Exporting

The download menu a chart can offer — the picture as a PNG or a PDF, and the numbers behind it as a CSV.

The download menu

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:

Loading editor

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.

What each file is

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. It is also what lets the picture be composed differently from the chart — see Composing the picture.

CSV is the numbers, one row per category and one column per series:

"Month","Email","SMS"
"Jan",42,9
"Feb",58,12
"Mar",76,16

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.

Narrowing the menu

exportFormats decides which files are offered, and in which order:

<Chart
	ariaLabel="Email opens per day"
	exportFormats={['png']}
	marks={marks}
	withExport={true}
/>

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.

Composing the picture

A file leaves the page it was read on, and the page is where a lot of a chart's meaning lives — the legend in the sidebar beside it, the surface it is read against, the heading over it. None of that comes with the picture. exportImage says what the picture needs that the screen didn't; it reaches the PNG and the PDF and nothing else, and never changes what the reader is looking at.

withLegend is the one most charts need. A chart whose colors are named somewhere else on the page downloads as an unlabelled picture, so this puts a legend in the drawing without putting one on the screen:

<Chart
	ariaLabel="Messages delivered per month by channel"
	exportImage={{ withLegend: true }}
	marks={marks}
	withExport={true}
	withLegend={false}
/>

The picture is drawn at the size the chart is on screen, so a legend added here takes its band out of the plot rather than making the file taller. legendPlacement alongside it moves that band, and is only worth setting when the picture is drawing a legend the chart hasn't.

backgroundColor is what the picture is painted on. Left alone it takes the surface the chart was being read against, which is the closest thing to what the reader saw — a chart's own background is transparent, and a transparent PNG dropped into a dark document is an invisible chart. Set it where the file has a destination of its own:

<Chart ariaLabel="Messages delivered" exportImage={{ backgroundColor: '#ffffff' }} marks={marks} withExport={true} />

'transparent' is honored as asked for in a PNG, and hands back a picture to composite rather than one to look at. A PDF page is paper and carries no transparency, so a transparent or translucent background is composited onto the chart's surface color there rather than arriving black.

What the files are called

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.

What a download costs

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.

Known limits

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.