Skip to content
Open
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/data/charts/export/ExportChartAsSvg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import { LineChartPro } from '@mui/x-charts-pro/LineChartPro';
import { useChartProApiRef } from '@mui/x-charts-pro/hooks';

export default function ExportChartAsSvg() {
const apiRef = useChartProApiRef();

return (
<Stack sx={{ width: '100%', gap: 2 }}>
<LineChartPro
apiRef={apiRef}
xAxis={[{ data: [1, 2, 3, 5, 8, 10] }]}
series={[
{ data: [4, 9, 1, 4, 9, 6], label: 'Series A' },
{ data: [2, 5.5, 2, 8.5, 1.5, 5], area: true, label: 'Series B' },
]}
height={300}
grid={{ vertical: true, horizontal: true }}
/>
<div>
<Button
onClick={() => apiRef.current.exportAsSvg({ fileName: 'chart' })}
variant="contained"
>
Export SVG
</Button>
</div>
</Stack>
);
}
32 changes: 32 additions & 0 deletions docs/data/charts/export/ExportChartAsSvg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import Button from '@mui/material/Button';
import Stack from '@mui/material/Stack';
import { LineChartPro } from '@mui/x-charts-pro/LineChartPro';
import { useChartProApiRef } from '@mui/x-charts-pro/hooks';

export default function ExportChartAsSvg() {
const apiRef = useChartProApiRef<'line'>();

return (
<Stack sx={{ width: '100%', gap: 2 }}>
<LineChartPro
apiRef={apiRef}
xAxis={[{ data: [1, 2, 3, 5, 8, 10] }]}
series={[
{ data: [4, 9, 1, 4, 9, 6], label: 'Series A' },
{ data: [2, 5.5, 2, 8.5, 1.5, 5], area: true, label: 'Series B' },
]}
height={300}
grid={{ vertical: true, horizontal: true }}
/>
<div>
<Button
onClick={() => apiRef.current!.exportAsSvg({ fileName: 'chart' })}
variant="contained"
>
Export SVG
</Button>
</div>
</Stack>
);
}
38 changes: 31 additions & 7 deletions docs/data/charts/export/export.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: Charts - Export
productId: x-charts
components: ScatterChartPro, BarChartPro, LineChartPro, Heatmap, FunnelChart, RadarChartPro, SankeyChart
components: ScatterChartPro, BarChartPro, LineChartPro, Heatmap, FunnelChart, RadarChartPro, SankeyChart, ChartsToolbarSvgExportTrigger
---

# Charts - Export [<span class="plan-pro"></span>](/x/introduction/licensing/#pro-plan 'Pro plan')

<p class="description">Let users export a chart as an image or in PDF format.</p>

Charts can be exported as images, or as PDFs using the browser's native print dialog.
Charts can be exported as images, as SVG files, or as PDFs using the browser's native print dialog.
The exporting feature is available for the following charts:

- `LineChartPro`
Expand Down Expand Up @@ -61,23 +61,25 @@ yarn add rasterizehtml

## Export options

Export behavior can be modified with [print](/x/api/charts/chart-print-export-options/) and [image export](/x/api/charts/chart-image-export-options/) options.
Export behavior can be modified with [print](/x/api/charts/chart-print-export-options/), [image export](/x/api/charts/chart-image-export-options/), and [SVG export](/x/api/charts/chart-svg-export-options/) options.
These options can be passed to the built-in toolbar using `slotProps.toolbar`, and are then automatically displayed.

You can customize their respective behaviors by passing an options object to `slotProps.toolbar`, or to the export trigger itself if you're using a custom toolbar:

```tsx
// Default toolbar:
<BarChartPro slotProps={{ toolbar: { printOptions, imageExportOptions } }} />
<BarChartPro slotProps={{ toolbar: { printOptions, imageExportOptions, svgExportOptions } }} />

// Custom trigger:
<ChartsToolbarImageExportTrigger options={imageExportOptions} />
<ChartsToolbarPrintExportTrigger options={printExportOptions} />
<ChartsToolbarSvgExportTrigger options={svgExportOptions} />
```

### Export formats

To disable the print export, set the `disableToolbarButton` property to `true`.
To disable the print export, set the `disableToolbarButton` property to `true` on `printOptions`.
The SVG export can be disabled the same way through `svgExportOptions`.

You can customize image export formats by providing an array of objects to the `imageExportOptions` property.
These objects must contain the `type` property which specifies the image format.
Expand All @@ -95,8 +97,8 @@ The name of the exported file has been customized to resemble the chart's title.

To add custom styles or modify the chart's appearance before exporting, use the `onBeforeExport` callback.

When exporting, the chart is first rendered into an iframe and then exported as an image or PDF.
The `onBeforeExport` callback gives you access to the iframe before the export process starts.
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.
Comment on lines +100 to +101

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


For example, you can add the title and caption to the exported chart as shown below:

Expand Down Expand Up @@ -177,3 +179,25 @@ apiRef.current?.exportAsImage({ pixelRatio: 3 });
```

{{"demo": "ExportChartAsImage.js"}}

### Export as SVG

The `apiRef` prop also exposes the `exportAsSvg()` method to export the chart as a standalone SVG file.
Unlike image export, this requires no extra dependency.

The output is vector-based, so it stays crisp at any size and remains editable in design tools such as Figma.
Series rendered to a canvas (for example, the WebGL renderer) cannot be vectorized and are embedded as a raster image inside the SVG, while the rest of the chart (axes, labels, legend) stays vector.

```tsx
apiRef.current?.exportAsSvg({ fileName: 'my-chart' });
```

{{"demo": "ExportChartAsSvg.js"}}

:::info
The chart is always exported in light mode, regardless of the color scheme of the page it is rendered in.
:::

Comment on lines +197 to +200

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

:::warning
Styles served from a cross-origin stylesheet cannot be read for security reasons and are omitted from the exported SVG, the same way they are for image export.
:::
24 changes: 12 additions & 12 deletions docs/data/charts/localization/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,48 @@
"languageTag": "fr-FR",
"importName": "frFR",
"localeName": "French",
"missingKeysCount": 18,
"totalKeysCount": 120,
"missingKeysCount": 19,
"totalKeysCount": 121,
"githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-charts/src/locales/frFR.ts"
},
{
"languageTag": "el-GR",
"importName": "elGR",
"localeName": "Greek",
"missingKeysCount": 17,
"totalKeysCount": 120,
"missingKeysCount": 18,
"totalKeysCount": 121,
"githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-charts/src/locales/elGR.ts"
},
{
"languageTag": "nb-NO",
"importName": "nbNO",
"localeName": "Norwegian (Bokmål)",
"missingKeysCount": 17,
"totalKeysCount": 120,
"missingKeysCount": 18,
"totalKeysCount": 121,
"githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-charts/src/locales/nbNO.ts"
},
{
"languageTag": "pt-PT",
"importName": "ptPT",
"localeName": "Portuguese",
"missingKeysCount": 13,
"totalKeysCount": 120,
"missingKeysCount": 14,
"totalKeysCount": 121,
"githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-charts/src/locales/ptPT.ts"
},
{
"languageTag": "pt-BR",
"importName": "ptBR",
"localeName": "Portuguese (Brazil)",
"missingKeysCount": 17,
"totalKeysCount": 120,
"missingKeysCount": 18,
"totalKeysCount": 121,
"githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-charts/src/locales/ptBR.ts"
},
{
"languageTag": "sv-SE",
"importName": "svSE",
"localeName": "Swedish",
"missingKeysCount": 113,
"totalKeysCount": 120,
"missingKeysCount": 114,
"totalKeysCount": 121,
"githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-charts/src/locales/svSE.ts"
}
]
5 changes: 5 additions & 0 deletions docs/data/chartsApiPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ const chartsApiPages: MuiPage[] = [
title: 'ChartsToolbarRangeButtonTrigger',
plan: 'pro',
},
{
pathname: '/x/api/charts/charts-toolbar-svg-export-trigger',
title: 'ChartsToolbarSvgExportTrigger',
plan: 'pro',
},
{
pathname: '/x/api/charts/charts-toolbar-zoom-in-trigger',
title: 'ChartsToolbarZoomInTrigger',
Expand Down
22 changes: 22 additions & 0 deletions docs/pages/x/api/charts/chart-svg-export-options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from 'react';
import InterfaceApiPage from 'docs/src/modules/components/InterfaceApiPage';
import layoutConfig from 'docs/src/modules/utils/dataGridLayoutConfig';
import { mapApiPageTranslations } from '@mui/internal-core-docs/mapApiPageTranslations';
import jsonPageContent from './chart-svg-export-options.json';

export default function Page(props) {
const { descriptions } = props;
return (
<InterfaceApiPage {...layoutConfig} descriptions={descriptions} pageContent={jsonPageContent} />
);
}

export async function getStaticProps() {
const req = require.context(
'docs/translations/api-docs/charts/',
false,
/\.\/chart-svg-export-options.*.json$/,
);
const descriptions = mapApiPageTranslations(req);
return { props: { descriptions } };
}
18 changes: 18 additions & 0 deletions docs/pages/x/api/charts/chart-svg-export-options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "ChartSvgExportOptions",
"imports": ["import { ChartSvgExportOptions } from '@mui/x-charts-pro'"],
"demos": "<ul><li><a href=\"https://mui.com/x/react-charts/export/#export-as-svg\">SVG export</a></li></ul>",
"properties": {
"copyStyles": { "type": { "description": "boolean" }, "default": "true", "isProPlan": true },
"fileName": {
"type": { "description": "string" },
"default": "The title of the document the chart belongs to",
"isProPlan": true
},
"nonce": { "type": { "description": "string" }, "isProPlan": true },
"onBeforeExport": {
"type": { "description": "(svg: SVGElement) =&gt; Promise&lt;void&gt; | void" },
"isProPlan": true
}
}
}
20 changes: 20 additions & 0 deletions docs/pages/x/api/charts/charts-toolbar-svg-export-trigger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import { ApiPage } from '@mui/internal-core-docs/ApiPage';
import { mapApiPageTranslations } from '@mui/internal-core-docs/mapApiPageTranslations';
import jsonPageContent from './charts-toolbar-svg-export-trigger.json';

export default function Page(props) {
const { descriptions } = props;
return <ApiPage descriptions={descriptions} pageContent={jsonPageContent} />;
}

export async function getStaticProps() {
const req = require.context(
'docs/translations/api-docs/charts/charts-toolbar-svg-export-trigger',
false,
/\.\/charts-toolbar-svg-export-trigger.*\.json$/,
);
const descriptions = mapApiPageTranslations(req);

return { props: { descriptions } };
}
14 changes: 14 additions & 0 deletions docs/pages/x/api/charts/charts-toolbar-svg-export-trigger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"props": {},
"name": "ChartsToolbarSvgExportTrigger",
"imports": [
"import { ChartsToolbarSvgExportTrigger } from '@mui/x-charts-pro/ChartsToolbarPro';",
"import { ChartsToolbarSvgExportTrigger } from '@mui/x-charts-pro';"
],
"classes": [],
"muiName": "MuiChartsToolbarSvgExportTrigger",
"filename": "/packages/x-charts-pro/src/ChartsToolbarPro/ChartsToolbarSvgExportTrigger.tsx",
"inheritance": null,
"demos": "<ul><li><a href=\"/x/react-charts/export/\">Charts - Export <a href=\"/x/introduction/licensing/#pro-plan\" title=\"Pro plan\"><span class=\"plan-pro\"></span></a></a></li></ul>",
"cssComponent": false
}
1 change: 1 addition & 0 deletions docs/scripts/createXTypeScriptProjects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export const interfacesToDocument: InterfacesToDocumentType[] = [
'AxisConfig',
'ChartImageExportOptions',
'ChartPrintExportOptions',
'ChartSvgExportOptions',
'LegendItemParams',
],
},
Expand Down
15 changes: 15 additions & 0 deletions docs/translations/api-docs/charts/chart-svg-export-options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"interfaceDescription": "The options to apply on the SVG export.",
"propertiesDescriptions": {
"copyStyles": {
"description": "If <code>true</code>, the styles of the page the chart belongs to will be copied to the export iframe.<br />Copying styles is useful to ensure that the exported chart looks the same as it does on the page."
},
"fileName": { "description": "The name of the file without the extension." },
"nonce": {
"description": "A nonce to be used for Content Security Policy (CSP) compliance.<br />If provided, this nonce will be added to any style elements created during the export process."
},
"onBeforeExport": {
"description": "Callback function that is called before the export is triggered.<br />It receives the SVG element to be exported, so it can be modified before serialization<br />(such as adding elements, updating styles, removing elements, etc.)."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"componentDescription": "A button that triggers an SVG export.\nIt renders the `baseButton` slot.",
"propDescriptions": {},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ BarChartPremium.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
setAxisZoomData: PropTypes.func.isRequired,
setZoomData: PropTypes.func.isRequired,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ CandlestickChart.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
setAxisZoomData: PropTypes.func.isRequired,
setZoomData: PropTypes.func.isRequired,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ HeatmapPremium.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
setAxisZoomData: PropTypes.func.isRequired,
setZoomData: PropTypes.func.isRequired,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ RadialBarChart.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
}),
}),
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ RadialLineChart.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
}),
}),
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ ScatterChartPremium.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
setAxisZoomData: PropTypes.func.isRequired,
setZoomData: PropTypes.func.isRequired,
}),
Expand Down
1 change: 1 addition & 0 deletions packages/x-charts-pro/src/BarChartPro/BarChartPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ BarChartPro.propTypes = {
current: PropTypes.shape({
exportAsImage: PropTypes.func.isRequired,
exportAsPrint: PropTypes.func.isRequired,
exportAsSvg: PropTypes.func.isRequired,
setAxisZoomData: PropTypes.func.isRequired,
setZoomData: PropTypes.func.isRequired,
}),
Expand Down
Loading
Loading