Charts in JavaScript Excel Library

26 Oct 202624 minutes to read

Charts turn worksheet data into visual comparisons that open in Excel. Place a chart on the sheet, point it at a data range, and set the chart type to match the story you want to show.

sheet.addChart(type, row, column, width, height, data, values?)
  • type — a member of ChartType.
  • row / column — 1-based top-left anchor.
  • width / height — size in points.
  • data — A1 range string, category formula string(s), or ChartData. Pass '' for an empty chart and add series later with Chart.addSeries.
  • values — optional values formula when data holds category labels.

The snippets on this page save the workbook with await workbook.save('<file name>.xlsx'), which requires Node.js. In a browser, call workbook.save() without arguments and download the returned bytes, as shown in Getting started.

Column chart

A column chart compares values across categories as vertical bars. Clustering related series side by side makes it easy to contrast groups in the same chart.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

sheet.cell('A1').value = 'Month';
sheet.cell('B1').value = 'Sales';
sheet.cell('A2').value = 'Jan';
sheet.cell('B2').number = 120;
sheet.cell('A3').value = 'Feb';
sheet.cell('B3').number = 150;

const chart = sheet.addChart(
  ChartType.ColumnClustered,
  2,
  4,
  400,
  250,
  'A1:B3',
);

chart.chartTitle = 'Monthly sales';
// Optional: chart.hasTitle = false to hide the title while keeping text
// chart.chartTitleArea controls title formatting (font, fill, alignment)

// Save the workbook.
await workbook.save('ColumnChart.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

sheet.cell('A1').value = 'Month';
sheet.cell('B1').value = 'Sales';
sheet.cell('A2').value = 'Jan';
sheet.cell('B2').number = 120;
sheet.cell('A3').value = 'Feb';
sheet.cell('B3').number = 150;

const chart = sheet.addChart(
  ChartType.ColumnClustered,
  2,
  4,
  400,
  250,
  'A1:B3',
);

chart.chartTitle = 'Monthly sales';

// Save the workbook.
await workbook.save('ColumnChart.xlsx');

The chart title property is chart.chartTitle (not title). Series and axes use their own title properties separately.

Bar chart

A bar chart compares values across categories as horizontal bars. Use ChartType.BarClustered, ChartType.BarStacked, or ChartType.BarPercentStacked.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.BarClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Save the workbook.
await workbook.save('BarChart.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.BarClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Save the workbook.
await workbook.save('BarChart.xlsx');

Line chart

A line chart shows how values change across ordered categories, such as months. Use ChartType.Line for lines, or ChartType.LineMarkers to add markers to each point.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.LineMarkers, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales trend';

// Save the workbook.
await workbook.save('LineChart.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.LineMarkers, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales trend';

// Save the workbook.
await workbook.save('LineChart.xlsx');

Pie and doughnut charts

Pie and doughnut charts show how each category contributes to a total. They use one data series. Use ChartType.PieExploded or ChartType.DoughnutExploded to pull the slices apart, or set explodePercent on the series.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

// Pie chart of the 2026 column
const pie = sheet.addChart(ChartType.Pie, 2, 5, 360, 240, 'A1:A5');
const pieSeries = pie.addSeries('2026');
pieSeries.categoryLabels = 'Sheet1!$A$2:$A$5';
pieSeries.values = 'Sheet1!$C$2:$C$5';
pieSeries.explodePercent = 10;
pie.chartTitle = '2026 sales share';

// Doughnut chart of the same data
const doughnut = sheet.addChart(ChartType.Doughnut, 20, 5, 360, 240, 'A1:A5');
const doughnutSeries = doughnut.addSeries('2026');
doughnutSeries.categoryLabels = 'Sheet1!$A$2:$A$5';
doughnutSeries.values = 'Sheet1!$C$2:$C$5';
doughnut.chartTitle = '2026 sales share';

// Save the workbook.
await workbook.save('PieAndDoughnutCharts.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

// Pie chart of the 2026 column
const pie = sheet.addChart(ChartType.Pie, 2, 5, 360, 240, 'A1:A5');
const pieSeries = pie.addSeries('2026');
pieSeries.categoryLabels = 'Sheet1!$A$2:$A$5';
pieSeries.values = 'Sheet1!$C$2:$C$5';
pieSeries.explodePercent = 10;
pie.chartTitle = '2026 sales share';

// Doughnut chart of the same data
const doughnut = sheet.addChart(ChartType.Doughnut, 20, 5, 360, 240, 'A1:A5');
const doughnutSeries = doughnut.addSeries('2026');
doughnutSeries.categoryLabels = 'Sheet1!$A$2:$A$5';
doughnutSeries.values = 'Sheet1!$C$2:$C$5';
doughnut.chartTitle = '2026 sales share';

// Save the workbook.
await workbook.save('PieAndDoughnutCharts.xlsx');

Area chart

An area chart emphasizes the magnitude of change over time by filling the area under each line. Use ChartType.Area, ChartType.AreaStacked, or ChartType.AreaPercentStacked.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.AreaStacked, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Cumulative sales';

// Save the workbook.
await workbook.save('AreaChart.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.AreaStacked, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Cumulative sales';

// Save the workbook.
await workbook.save('AreaChart.xlsx');

Scatter chart

A scatter chart plots pairs of numbers to show the relationship between two variables. Choose the look with the chart type, for example ChartType.ScatterMarkers, ChartType.ScatterLine, ChartType.ScatterLineMarkers, ChartType.ScatterSmooth, or ChartType.ScatterSmoothMarkers. You can also set the scatterStyle property to none, line, lineMarker, marker, smooth, or smoothMarker.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

// X values from column B (2025), Y values from column C (2026)
const chart = sheet.addChart(ChartType.ScatterMarkers, 2, 5, 400, 250, '');
const series = chart.addSeries('2026 versus 2025');
series.categoryLabels = 'Sheet1!$B$2:$B$5';
series.values = 'Sheet1!$C$2:$C$5';
chart.primaryCategoryAxis.title = '2025';
chart.primaryValueAxis.title = '2026';

// Connect the points with smooth lines and keep the markers
chart.scatterStyle = 'smoothMarker';

// Save the workbook.
await workbook.save('ScatterChart.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

// X values from column B (2025), Y values from column C (2026)
const chart = sheet.addChart(ChartType.ScatterMarkers, 2, 5, 400, 250, '');
const series = chart.addSeries('2026 versus 2025');
series.categoryLabels = 'Sheet1!$B$2:$B$5';
series.values = 'Sheet1!$C$2:$C$5';
chart.primaryCategoryAxis.title = '2025';
chart.primaryValueAxis.title = '2026';

// Connect the points with smooth lines and keep the markers
chart.scatterStyle = 'smoothMarker';

// Save the workbook.
await workbook.save('ScatterChart.xlsx');

Bubble chart

A bubble chart adds a third value to a scatter chart by sizing each marker. Set bubbleSizes on the series to the range that holds the sizes. Use ChartType.Bubble3D for a three-dimensional look.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.Bubble, 2, 5, 400, 250, '');
const series = chart.addSeries('Sales');
series.categoryLabels = 'Sheet1!$B$2:$B$5';
series.values = 'Sheet1!$C$2:$C$5';
series.bubbleSizes = 'Sheet1!$B$2:$B$5';
chart.chartTitle = 'Sales bubbles';

// Save the workbook.
await workbook.save('BubbleChart.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.Bubble, 2, 5, 400, 250, '');
const series = chart.addSeries('Sales');
series.categoryLabels = 'Sheet1!$B$2:$B$5';
series.values = 'Sheet1!$C$2:$C$5';
series.bubbleSizes = 'Sheet1!$B$2:$B$5';
chart.chartTitle = 'Sales bubbles';

// Save the workbook.
await workbook.save('BubbleChart.xlsx');

Stacked and percent-stacked grouping

Column, bar, and area charts can be clustered, stacked, or percent stacked. Pick the matching chart type, or change the grouping property of an existing chart to clustered, stacked, percentStacked, or standard. Area charts accept standard, stacked, and percentStacked.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Stack the series instead of clustering them
chart.grouping = 'stacked';

// Save the workbook.
await workbook.save('ChartGrouping.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Stack the series instead of clustering them
chart.grouping = 'stacked';

// Save the workbook.
await workbook.save('ChartGrouping.xlsx');

Three-dimensional charts

Three-dimensional chart types, such as ChartType.ColumnClustered3D, ChartType.Pie3D, and ChartType.Area3D, support the view3D property for rotation, perspective, and depth.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered3D, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = '3-D sales';

chart.view3D = {
  rotationX: 20,
  rotationY: 30,
  perspective: 30,
  rightAngleAxis: false,
};

// Save the workbook.
await workbook.save('ThreeDimensionalCharts.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered3D, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = '3-D sales';

chart.view3D = {
  rotationX: 20,
  rotationY: 30,
  perspective: 30,
  rightAngleAxis: false,
};

// Save the workbook.
await workbook.save('ThreeDimensionalCharts.xlsx');

Funnel, waterfall, and other extended charts

Extended chart types are ChartType.Funnel, ChartType.Waterfall, ChartType.Pareto, ChartType.BoxAndWhisker, ChartType.Sunburst, and ChartType.TreeMap. For these types, pass the category labels and the values to addChart as two formulas. Do not pass a single data range, and do not change a chart between a standard type and an extended type. Create a new chart instead.

import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const funnel = sheet.addChart(
  ChartType.Funnel,
  2,
  5,
  400,
  250,
  'Sheet1!$A$2:$A$5',
  'Sheet1!$C$2:$C$5',
);
funnel.chartTitle = '2026 sales funnel';

const waterfall = sheet.addChart(
  ChartType.Waterfall,
  20,
  5,
  400,
  250,
  'Sheet1!$A$2:$A$5',
  'Sheet1!$C$2:$C$5',
);
waterfall.chartTitle = '2026 sales waterfall';

// Save the workbook.
await workbook.save('ExtendedCharts.xlsx');
import { Workbook, ChartType } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const funnel = sheet.addChart(
  ChartType.Funnel,
  2,
  5,
  400,
  250,
  'Sheet1!$A$2:$A$5',
  'Sheet1!$C$2:$C$5',
);
funnel.chartTitle = '2026 sales funnel';

const waterfall = sheet.addChart(
  ChartType.Waterfall,
  20,
  5,
  400,
  250,
  'Sheet1!$A$2:$A$5',
  'Sheet1!$C$2:$C$5',
);
waterfall.chartTitle = '2026 sales waterfall';

// Save the workbook.
await workbook.save('ExtendedCharts.xlsx');

Common chart types

Common chart types cover column, bar, line, pie, area, scatter, and related layouts. Choose the type that best matches how you want categories and values compared.

  • Column: ColumnClustered, ColumnStacked, ColumnPercentStacked
  • Bar: BarClustered, BarStacked, BarPercentStacked
  • Line: Line, LineStacked, LineMarkers, and related stacked/marker variants
  • Pie and doughnut: Pie, PieExploded, Doughnut, PieOfPie, BarOfPie
  • Scatter and bubble: ScatterMarkers, ScatterLine, Bubble
  • Area: Area, AreaStacked, AreaPercentStacked
  • 3-D layouts: Column3D, Pie3D, Line3D, and related members
  • Extended: Funnel, Waterfall, BoxAndWhisker, Pareto, Sunburst, TreeMap

Configure series, legend, and axes

After a chart exists, you can refine series, axes, the legend, and data labels so the visual matches your report. Titles and layout options help readers understand each series at a glance.

  • addSeries(title?) appends a series; set categoryLabels and values on the returned ChartSeries.
  • Axis titles use primaryCategoryAxis.title / primaryValueAxis.title (not chartTitle).
  • Toggle the legend with hasLegend and position it through legend.
  • Remove a chart with sheet.removeChart(chart) or by zero-based index.
import { Workbook, ChartType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

sheet.cell('A1').value = 'Q';
sheet.cell('B1').value = 'Rev';
sheet.cell('A2').value = 'Q1';
sheet.cell('B2').number = 10;
sheet.cell('A3').value = 'Q2';
sheet.cell('B3').number = 14;

const chart = sheet.addChart(ChartType.Line, 2, 4, 360, 220, '');
chart.chartTitle = 'Revenue';
chart.hasLegend = true;
chart.legend.position = LegendPosition.Bottom;

const series = chart.addSeries('Rev');
series.categoryLabels = 'Sheet1!$A$2:$A$3';
series.values = 'Sheet1!$B$2:$B$3';

chart.primaryCategoryAxis.title = 'Quarter';
chart.primaryValueAxis.title = 'Amount';

// Save the workbook.
await workbook.save('ConfigureSeriesLegendAndAxes.xlsx');
import { Workbook, ChartType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

sheet.cell('A1').value = 'Q';
sheet.cell('B1').value = 'Rev';
sheet.cell('A2').value = 'Q1';
sheet.cell('B2').number = 10;
sheet.cell('A3').value = 'Q2';
sheet.cell('B3').number = 14;

const chart = sheet.addChart(ChartType.Line, 2, 4, 360, 220, '');
chart.chartTitle = 'Revenue';
chart.hasLegend = true;
chart.legend.position = LegendPosition.Bottom;

const series = chart.addSeries('Rev');
series.categoryLabels = 'Sheet1!$A$2:$A$3';
series.values = 'Sheet1!$B$2:$B$3';

chart.primaryCategoryAxis.title = 'Quarter';
chart.primaryValueAxis.title = 'Amount';

// Save the workbook.
await workbook.save('ConfigureSeriesLegendAndAxes.xlsx');

Chart drawing parts are written when you save the workbook.

Data labels

Data labels show the value, category name, series name, or percentage next to each point. Set the label options on the series and choose the position with DataLabelPosition.

import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

const labels = chart.series[0].dataLabels;
labels.isValue = true;
labels.isCategoryName = false;
labels.isSeriesName = false;
labels.isPercentage = false;
labels.position = DataLabelPosition.OutsideEnd;
labels.textArea.size = 10;
labels.textArea.bold = true;

// Save the workbook.
await workbook.save('DataLabels.xlsx');
import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

const labels = chart.series[0].dataLabels;
labels.isValue = true;
labels.isCategoryName = false;
labels.isSeriesName = false;
labels.isPercentage = false;
labels.position = DataLabelPosition.OutsideEnd;
labels.textArea.size = 10;
labels.textArea.bold = true;

// Save the workbook.
await workbook.save('DataLabels.xlsx');

The DataLabelPosition object has Center, InsideBase, InsideEnd, OutsideEnd, BestFit, Left, Right, Top, and Bottom. Not every position applies to every chart type.

Chart, series, and text formatting

Format the chart area, plot area, series, and text. Colors are RRGGBB or #RRGGBB strings, line weights are in points, and fill transparency is a number from 0 (opaque) to 100.

import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');

// Title text
chart.chartTitle = 'Sales by region';
chart.chartTitleArea.size = 16;
chart.chartTitleArea.bold = true;
chart.chartTitleArea.color = '1F3864';

// Chart area and plot area
chart.chartArea.fill.type = ChartFillType.Solid;
chart.chartArea.fill.foreColor = 'F2F2F2';
chart.chartArea.line.color = 'BFBFBF';
chart.chartArea.line.weightPt = 1;
chart.plotArea.fill.type = ChartFillType.None;

// First series
const format = chart.series[0].serieFormat;
format.fill.type = ChartFillType.Solid;
format.fill.foreColor = '4472C4';
format.fill.transparency = 10;
format.line.color = '2F5597';
format.line.weightPt = 1.5;

// Save the workbook.
await workbook.save('ChartFormatting.xlsx');
import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');

// Title text
chart.chartTitle = 'Sales by region';
chart.chartTitleArea.size = 16;
chart.chartTitleArea.bold = true;
chart.chartTitleArea.color = '1F3864';

// Chart area and plot area
chart.chartArea.fill.type = ChartFillType.Solid;
chart.chartArea.fill.foreColor = 'F2F2F2';
chart.chartArea.line.color = 'BFBFBF';
chart.chartArea.line.weightPt = 1;
chart.plotArea.fill.type = ChartFillType.None;

// First series
const format = chart.series[0].serieFormat;
format.fill.type = ChartFillType.Solid;
format.fill.foreColor = '4472C4';
format.fill.transparency = 10;
format.line.color = '2F5597';
format.line.weightPt = 1.5;

// Save the workbook.
await workbook.save('ChartFormatting.xlsx');

Axis options

Use the primaryCategoryAxis and primaryValueAxis objects to set titles, scale limits, number formats, gridlines, tick marks, and visibility.

import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');

const valueAxis = chart.primaryValueAxis;
valueAxis.title = 'Sales';
valueAxis.minimumValue = 0;
valueAxis.maximumValue = 200;
valueAxis.numberFormat = '#,##0';
valueAxis.hasMajorGridLines = true;
valueAxis.hasMinorGridLines = false;
valueAxis.majorTickMark = TickMarkType.Outside;
valueAxis.minorTickMark = TickMarkType.None;

const categoryAxis = chart.primaryCategoryAxis;
categoryAxis.title = 'Region';
categoryAxis.titleArea.italic = true;
categoryAxis.line.color = '7F7F7F';

// Hide an axis

// Save the workbook.
await workbook.save('ChartAxes.xlsx');
import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');

const valueAxis = chart.primaryValueAxis;
valueAxis.title = 'Sales';
valueAxis.minimumValue = 0;
valueAxis.maximumValue = 200;
valueAxis.numberFormat = '#,##0';
valueAxis.hasMajorGridLines = true;
valueAxis.hasMinorGridLines = false;
valueAxis.majorTickMark = TickMarkType.Outside;
valueAxis.minorTickMark = TickMarkType.None;

const categoryAxis = chart.primaryCategoryAxis;
categoryAxis.title = 'Region';
categoryAxis.titleArea.italic = true;
categoryAxis.line.color = '7F7F7F';

// Hide an axis

// Save the workbook.
await workbook.save('ChartAxes.xlsx');

The TickMarkType object has None, Inside, Outside, and Cross.

Legend, data table, and position

Show or hide the legend, move it with LegendPosition, show a data table under the plot, and place the chart on the worksheet. The row and column properties are 1-based anchors, and top and left are distances in points from the top-left corner of the sheet.

import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Legend
chart.hasLegend = true;
chart.legend.position = LegendPosition.Right;
chart.legend.textArea.size = 10;

// Data table under the plot
chart.hasDataTable = true;

// Move and resize the chart
chart.left = 320;
chart.top = 40;
chart.width = 480;
chart.height = 300;

// Save the workbook.
await workbook.save('ChartLegendAndPosition.xlsx');
import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Legend
chart.hasLegend = true;
chart.legend.position = LegendPosition.Right;
chart.legend.textArea.size = 10;

// Data table under the plot
chart.hasDataTable = true;

// Move and resize the chart
chart.left = 320;
chart.top = 40;
chart.width = 480;
chart.height = 300;

// Save the workbook.
await workbook.save('ChartLegendAndPosition.xlsx');

The LegendPosition object has Bottom, Top, Left, Right, and Corner.

Manage series and charts

Add or remove series, switch whether series are read from rows or columns, and remove a chart from the worksheet.

import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook: Workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Read series from rows instead of columns
chart.isSeriesInRows = true;

// Add a series, then remove it by reference or by zero-based index
const extra = chart.addSeries('Target');
extra.values = 'Sheet1!$C$2:$C$5';
chart.removeSeries(extra);

// Remove a second chart by reference or by zero-based index
const second = sheet.addChart(ChartType.Line, 20, 5, 400, 250, 'A1:C5');
sheet.removeChart(second);

// Save the workbook.
await workbook.save('ManageCharts.xlsx');
import { Workbook, ChartType, ChartFillType, DataLabelPosition, TickMarkType, LegendPosition } from '@syncfusion/ej2-xlsx';

const workbook = Workbook.create();
const sheet = workbook.sheet(0);

// Sample data in A1:C5
const rows = [
  ['Region', '2025', '2026'],
  ['North', 120, 150],
  ['South', 90, 110],
  ['East', 60, 95],
  ['West', 75, 80],
];
rows.forEach((row, r) => {
  row.forEach((value, c) => {
    const cell = sheet.cell(r + 1, c + 1);
    if (typeof value === 'number') {
      cell.number = value;
    } else {
      cell.value = value;
    }
  });
});

const chart = sheet.addChart(ChartType.ColumnClustered, 2, 5, 400, 250, 'A1:C5');
chart.chartTitle = 'Sales by region';

// Read series from rows instead of columns
chart.isSeriesInRows = true;

// Add a series, then remove it by reference or by zero-based index
const extra = chart.addSeries('Target');
extra.values = 'Sheet1!$C$2:$C$5';
chart.removeSeries(extra);

// Remove a second chart by reference or by zero-based index
const second = sheet.addChart(ChartType.Line, 20, 5, 400, 250, 'A1:C5');
sheet.removeChart(second);

// Save the workbook.
await workbook.save('ManageCharts.xlsx');