Skip to content

[charts] Export charts as SVG#22661

Open
sai6855 wants to merge 29 commits into
mui:masterfrom
sai6855:export-svg
Open

[charts] Export charts as SVG#22661
sai6855 wants to merge 29 commits into
mui:masterfrom
sai6855:export-svg

Conversation

@sai6855 sai6855 changed the title Export svg [charts] Export charts as SVG Jun 2, 2026
@sai6855 sai6855 added type: new feature Expand the scope of the product to solve a new problem. plan: Pro Impact at least one Pro user. plan: Premium Impact at least one Premium user. scope: charts Changes related to the charts. labels Jun 2, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Jun 2, 2026

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 🔺+33B(+0.01%) 🔺+9B(+0.01%)
@mui/x-charts-pro 🔺+4.53KB(+0.89%) 🔺+1.27KB(+0.83%)
@mui/x-charts-premium 🔺+4.53KB(+0.73%) 🔺+1.3KB(+0.70%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@sai6855 sai6855 marked this pull request as ready for review June 5, 2026 10:39
Copilot AI review requested due to automatic review settings June 5, 2026 10:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class SVG export support to MUI X Charts Pro/Premium, including a new toolbar trigger, API surface (exportAsSvg), localization key, and documentation/demo updates.

Changes:

  • Introduces exportAsSvg() in the Pro export plugin, including SVG serialization and canvas-layer raster embedding.
  • Adds a new ChartsToolbarSvgExportTrigger and integrates it into ChartsToolbarPro’s export menu + locale text.
  • Adds docs/demo content and an API reference page for the new toolbar trigger.

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/x-charts/src/locales/utils/chartsLocaleTextApi.ts Adds new locale key for SVG export menu item text.
packages/x-charts/src/locales/enUS.ts Provides English string for the new SVG export menu item.
packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/SankeyChart/SankeyChart.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/RadarChartPro/RadarChartPro.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/PieChartPro/PieChartPro.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/LineChartPro/LineChartPro.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/internals/plugins/useChartProExport/useChartProExport.types.ts Adds ChartSvgExportOptions and exportAsSvg to the public export API types.
packages/x-charts-pro/src/internals/plugins/useChartProExport/useChartProExport.ts Wires exportAsSvg into the export plugin public API and instance.
packages/x-charts-pro/src/internals/plugins/useChartProExport/exportSvg.ts Implements SVG export composition (SVG layers + rasterized canvas layers + legend serialization).
packages/x-charts-pro/src/internals/plugins/useChartProExport/exportImage.ts Refactors download triggering to shared helper.
packages/x-charts-pro/src/internals/plugins/useChartProExport/common.ts Introduces shared triggerDownload helper for export flows.
packages/x-charts-pro/src/Heatmap/Heatmap.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/FunnelChart/FunnelChart.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-pro/src/ChartsToolbarPro/index.ts Re-exports the new SVG export trigger component.
packages/x-charts-pro/src/ChartsToolbarPro/ChartsToolbarSvgExportTrigger.tsx Adds a new toolbar trigger component to call exportAsSvg.
packages/x-charts-pro/src/ChartsToolbarPro/ChartsToolbarPro.tsx Adds SVG export entry to the export menu and exposes svgExportOptions.
packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-premium/src/ScatterChartPremium/ScatterChartPremium.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-premium/src/RadialLineChart/RadialLineChart.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-premium/src/RadialBarChart/RadialBarChart.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-premium/src/HeatmapPremium/HeatmapPremium.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-premium/src/CandlestickChart/CandlestickChart.tsx Extends apiRef propTypes to include exportAsSvg.
packages/x-charts-premium/src/BarChartPremium/BarChartPremium.tsx Extends apiRef propTypes to include exportAsSvg.
docs/translations/api-docs/charts/charts-toolbar-svg-export-trigger/charts-toolbar-svg-export-trigger.json Adds translation stub for the new API page.
docs/pages/x/api/charts/charts-toolbar-svg-export-trigger.json Adds generated API docs metadata for the new component.
docs/pages/x/api/charts/charts-toolbar-svg-export-trigger.js Adds the new API docs page entry point.
docs/data/chartsApiPages.ts Registers the new API page in the charts API pages list.
docs/data/charts/export/ExportChartAsSvg.tsx Adds TS demo showcasing exportAsSvg.
docs/data/charts/export/ExportChartAsSvg.js Adds JS demo showcasing exportAsSvg.
docs/data/charts/export/export.md Documents SVG export and adds the new demo section.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +186 to +189
const blob = new Blob([svgString], { type: 'image/svg+xml' });
const url = URL.createObjectURL(blob);
triggerDownload(url, fileName || `${document.title}.svg`);
URL.revokeObjectURL(url);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed here 5c400bb

Comment on lines +44 to +49
function buildLegendGroup(
chartRoot: Element,
doc: Document,
originLeft: number,
originTop: number,
): SVGElement | null {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, we need some padding at the top though.

We might want to move this entire svg creation to an iframe too, so we can force the styling to be light mode, since currently running this in dark mode produces bad output.

Image

See the label text is different from the chart text

@sai6855 sai6855 Jun 8, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to iframe and forcing exporting to light mode, text and legend are rendering correctly now

image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried the export in dark mode from https://deploy-preview-22661--material-ui-x.netlify.app/x/react-charts/export/#default-toolbar

And legend text are white

image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be working for me

Comment on lines +100 to +101
For image and PDF export, `onBeforeExport` receives the iframe the chart is rendered into before the export process starts.
SVG export serializes an SVG document, so its `onBeforeExport` receives the `<svg>` element to be exported instead.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WOuld be easier if the onBeforeExport got the same iframe input, and add a warning in the SVG export section to say

:::warning
The SVG export is a serialization of the outer svg component.

If you CSS modification or add elements outside, they will have no impact ont the generated svg file
:::

Having same onBeforeExport signature should simplify sharing customization across different exports

Comment on lines +197 to +200
:::info
The chart is always exported in light mode, regardless of the color scheme of the page it is rendered in.
:::

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a general information impacting also PNG and PDF. Might be better to put it higher in the docs. And maybe add an issue to up-vote for dark mode support

Comment on lines 19 to +20
toolbarExportImage: (mimeType) => `Export as ${imageMimeTypes[mimeType] ?? mimeType}`,
toolbarExportSvg: 'Export as SVG',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHat about add image/svg+xml mimeType to avoid introducing toolbarExportSvg

Comment on lines +157 to +176
// Span the union of the plot and legend boxes; their top-left is the export origin.
const legendClone = chartClone.querySelector(`.${legendClasses.root}`);
const rects = [containerClone.getBoundingClientRect()];
if (legendClone) {
rects.push(legendClone.getBoundingClientRect());
}
const topPadding = legendClone ? LEGEND_TOP_PADDING : 0;
const originLeft = Math.min(...rects.map((rect) => rect.left));
const originTop = Math.min(...rects.map((rect) => rect.top)) - topPadding;
const width = Math.max(...rects.map((rect) => rect.right)) - originLeft;
const height = Math.max(...rects.map((rect) => rect.bottom)) - originTop;

const outSvg = exportDoc.createElementNS(SVG_NS, 'svg');
outSvg.setAttribute('xmlns', SVG_NS);
outSvg.setAttribute('xmlns:xlink', XLINK_NS);
outSvg.setAttribute('width', `${width}`);
outSvg.setAttribute('height', `${height}`);
outSvg.setAttribute('viewBox', `0 0 ${width} ${height}`);
outSvg.setAttribute('data-mui-color-scheme', 'light');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fairly limited. It assumes users only use our default legend with appropriate classes

Did you consider using the satori library to map https://github.com/vercel/satori ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe satori is not well suited because it's designer to run in node runtime. Here we are in the browser so we can leverage it to measure text position and style

Text will need an adaptation: color in HTML becomes fill in SVG

Instead of limiting ourself to the legend.root could we search for

  • all nodes with text content -> reproduce them with a <text/>
  • all nodes with border/backgroundColor -> reproduce them with a <rect />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all nodes with text content -> reproduce them with a
all nodes with border/backgroundColor -> reproduce them with a

It's an interesting approach, but should we also pass the classnames through?

I think the issue with trying to be too smart about it is that we can't really cover all ends unless we render exactly what the user provides with the classes they provide 😢

We can try though 🤷

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the issue with trying to be too smart about it is that we can't really cover all ends unless we render exactly what the user provides with the classes they provide 😢

Yes, especially with features in HTML/CSS not present in the SVG. For example: the text wrapping 🫣

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plan: Premium Impact at least one Premium user. plan: Pro Impact at least one Pro user. scope: charts Changes related to the charts. type: new feature Expand the scope of the product to solve a new problem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[charts] Export charts as SVG

4 participants