In data analysis scenarios, bubble charts help intuitively display multi-dimensional data relationships. Each data point in a bubble chart is defined by three values: X-axis value, Y-axis value, and bubble size. Spire.XLS for JavaScript provides rich charting APIs that support creating bubble charts directly in the browser via WebAssembly, without requiring a backend service.

This article covers two core features:

For installation and project configuration, please refer to Integrating Spire.XLS for JavaScript in a React Project. The following examples assume Spire.XLS is already installed and the WebAssembly module has been initialized.


Create a Bubble Chart

A bubble chart can be added to a worksheet using the sheet.Charts.Add() method. The specific steps are as follows:

  1. Create a Workbook object and get the first worksheet.
  2. Add a bubble chart via Charts.Add(ExcelChartType.Bubble).
  3. Set the DataRange property to specify the chart data area.
  4. Set SeriesDataFromRange = false to indicate that data is not obtained from row/column layout.
  5. Set Series[0].Bubbles to specify the bubble size data range.
  6. Set the chart title, position, and dimensions.
  7. Save the workbook via Workbook.SaveToFile().

The following is a complete code example that demonstrates creating a bubble chart in React:

function App() {
  const createBubbleChart = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the Excel file into VFS
    const inputFileName = 'CreateBubbleChart.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

    // Load the workbook
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile(inputFileName);

    // Get the first worksheet
    let sheet = workbook.Worksheets.get(0);

    // Add a bubble chart
    let chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.Bubble });

    // Set the chart data range
    chart.DataRange = sheet.Range.get("A1:C5");
    chart.SeriesDataFromRange = false;

    // Set the bubble sizes
    chart.Series.get(0).Bubbles = sheet.Range.get("C2:C5");

    // Set the chart position
    chart.LeftColumn = 7;
    chart.TopRow = 6;
    chart.RightColumn = 16;
    chart.BottomRow = 29;

    // Set the chart title
    chart.ChartTitle = "Bubble Chart";
    chart.ChartTitleArea.IsBold = true;
    chart.ChartTitleArea.Size = 12;

    // Save the workbook
    const outputFileName = 'CreateBubbleChart.xlsx';
    workbook.SaveToFile(outputFileName);

    // Dispose resources
    workbook.Dispose();

    // Read the converted file from VFS and trigger download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Create Bubble Chart</h1>
      <button onClick={createBubbleChart}>Start</button>
    </div>
  );
}

export default App;

After running the code, the effect of creating a bubble chart is as follows:

Create Bubble Chart


Style the Bubble Chart

After creating a bubble chart, you can enhance its appearance by setting the chart area, plot area, and series colors. The specific steps are as follows:

  1. Get the created bubble chart object.
  2. Set the ChartArea.Fill.ForeColor property to set the chart background color.
  3. Set the PlotArea.Fill.ForeColor property to set the plot area background color.
  4. Set the Series[0].Format.Fill.ForeColor property to set the series color.
  5. Set the Series[0].HasDataLabels property to enable data labels, and specify the label content through DataPoints.DefaultDataPoint.DataLabels.
  6. Save the workbook.

The following is a complete code example that demonstrates how to style a bubble chart:

function App() {
  const styleBubbleChart = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the Excel file into VFS
    const inputFileName = 'CreateBubbleChart.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/static/data/`);

    // Load the workbook
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile(inputFileName);

    // Get the first worksheet
    let sheet = workbook.Worksheets.get(0);

    // Add a bubble chart
    let chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.Bubble });

    // Set the chart data range
    chart.DataRange = sheet.Range.get("A1:C5");
    chart.SeriesDataFromRange = false;

    // Set the bubble sizes
    chart.Series.get(0).Bubbles = sheet.Range.get("C2:C5");

    // Set the chart position
    chart.LeftColumn = 7;
    chart.TopRow = 6;
    chart.RightColumn = 16;
    chart.BottomRow = 29;

    // Set the chart title
    chart.ChartTitle = "Bubble Chart";
    chart.ChartTitleArea.IsBold = true;
    chart.ChartTitleArea.Size = 12;

    // Style the bubble chart
    // Set chart area background color
    chart.ChartArea.Fill.ForeColor = xlsModule.Color.get_LightCyan();
    // Set plot area background color
    chart.PlotArea.Fill.ForeColor = xlsModule.Color.get_LightYellow();
    // Set series color
    chart.Series.get(0).Format.Fill.FillType = xlsModule.ShapeFillType.SolidColor;
    chart.Series.get(0).Format.Fill.ForeColor = xlsModule.Color.get_Orange();
    // Enable and set data labels
    chart.Series.get(0).HasDataLabels = true;
    chart.Series.get(0).DataPoints.DefaultDataPoint.DataLabels.HasCategoryName = true;
    chart.Series.get(0).DataPoints.DefaultDataPoint.DataLabels.HasValue = true;

    // Save the workbook
    const outputFileName = 'StyledBubbleChart.xlsx';
    workbook.SaveToFile(outputFileName);

    // Dispose resources
    workbook.Dispose();

    // Read the converted file from VFS and trigger download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Style Bubble Chart</h1>
      <button onClick={styleBubbleChart}>Start</button>
    </div>
  );
}

export default App;

After running the code, the effect of styling the bubble chart is as follows:

Style Bubble Chart


Frequently Asked Questions

What do columns A, B, and C in DataRange represent?

Reason: Each data point in a bubble chart needs three values — an X-axis value, a Y-axis value, and a bubble size — and the three columns specified by DataRange correspond to them exactly.

Solution: Taking chart.DataRange = sheet.Range.get("A1:C5") as an example, column A serves as the category (X axis), column B as the Y-axis value, and column C is set as the bubble size through chart.Series.get(0).Bubbles = sheet.Range.get("C2:C5"). The order of these three columns cannot be swapped, or both the point positions and the bubble sizes will be wrong.

Why does the data range start at A1 instead of A2?

Reason: The first row of the data range is used as the series name.

Solution: Include the header row in DataRange. In the example, the "Sales" shown in the legend comes from cell B1, so the data range is written as A1:C5 rather than A2:C5.


Get a Free License

Spire.XLS for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.

In data analysis scenarios, trendlines help you visually identify data trends and predict directions from Excel charts. Spire.XLS for JavaScript provides rich trendline APIs that allow you to add various types of trendlines to chart series directly in the browser via WebAssembly, without requiring a backend service.

This article covers two core features:

For installation and project configuration, please refer to Integrating Spire.XLS for JavaScript in a React Project. The following examples assume Spire.XLS is installed and the WebAssembly module has been initialized.


Add Trendline to Chart

You can add trendlines to any series in a chart through the chart.Series.get(i).TrendLines.Add() method. Spire.XLS for JavaScript supports 4 types of trendlines defined in the TrendLineType enumeration:

  • Linear — Linear trendline, suitable for data showing steady increase or decrease
  • Exponential — Exponential trendline, suitable for scenarios where the growth or decline rate accelerates
  • Logarithmic — Logarithmic trendline, suitable for data that changes rapidly then stabilizes
  • Moving_Average — Moving average trendline, suitable for smoothing data fluctuations

Specific steps:

  1. Create a Workbook object and get the first worksheet.
  2. Get the chart that needs a trendline through Worksheet.Charts.get(i).
  3. Call chart.Series.get(0).TrendLines.Add() with the type parameter specifying the trendline type.
  4. Save the workbook through Workbook.SaveToFile().

Below is a complete code example showing how to add four different types of trendlines to an Excel chart in React:

function App() {
  const addTrendline = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the Excel file into VFS
    const inputFileName = 'ChartTrendline_en.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // Get the first chart from the first worksheet
    let chart = workbook.Worksheets.get(0).Charts.get(0);

    // Add linear trendline
    chart.ChartTitle = "Linear Trendline";
    chart.Series.get(0).TrendLines.Add({ type: xlsModule.TrendLineType.Linear });

    // Add exponential trendline
    chart = workbook.Worksheets.get(0).Charts.get(0);
    chart.ChartTitle = "Exponential Trendline";
    chart.Series.get(0).TrendLines.Add({ type: xlsModule.TrendLineType.Exponential });

    // Add logarithmic trendline
    chart.ChartTitle = "Logarithmic Trendline";
    chart.Series.get(0).TrendLines.Add({ type: xlsModule.TrendLineType.Logarithmic });

    // Add moving average trendline
    chart.ChartTitle = "Moving Average Trendline";
    chart.Series.get(0).TrendLines.Add({ type: xlsModule.TrendLineType.Moving_Average });

    // Save the document
    const outputFileName = 'AddTrendline.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2010 });

    // Release resources
    workbook.Dispose();

    // Read the converted file from VFS and trigger download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Add Trendline to Chart</h1>
      <button onClick={addTrendline}>Start</button>
    </div>
  );
}

export default App;

After running the code, you get the effect of adding four different types of trendlines to a chart:

Add Trendline


Extract Trendline Formula

You can obtain the mathematical formula of a trendline through the trendLine.Formula property, making it easy to display the trendline's analytical expression in reports.

Specific steps:

  1. Load the AddTrendline.xlsx file generated in Step 1, which contains four charts.
  2. Use Worksheet.Charts.get(i) to iterate through all four charts.
  3. Read the trendLine.Formula property from each chart to obtain the formula string, then save all formulas to a text file.

Below is a complete code example showing how to extract trendline formulas in React:

function App() {
  const extractTrendlineFormula = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the AddTrendline.xlsx file generated in Step 1 into VFS
    const inputFileName = 'AddTrendline.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // Get the first worksheet
    let sheet = workbook.Worksheets.get(0);
    let result = "Extracted trendline formulas from four charts:\n\n";

    // Iterate through all four charts and extract trendline formulas
    for (let i = 0; i < 4; i++) {
      let chart = sheet.Charts.get(i);
      let trendLine = chart.Series.get(0).TrendLines.get(0);
      // Moving average trendline has no mathematical formula
      if (trendLine.Type === xlsModule.TrendLineType.Moving_Average) {
        result += `Chart ${i + 1} (${chart.ChartTitle}): N/A (Moving Average)\n`;
      } else {
        let formula = trendLine.Formula;
        result += `Chart ${i + 1} (${chart.ChartTitle}): ${formula}\n`;
      }
    }

    // Release resources
    workbook.Dispose();

    // Save the formulas to a text file and trigger download
    const outputFileName = 'ExtractTrendline.txt';
    const blob = new Blob([result], { type: "text/plain;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Extract Trendline Formula</h1>
      <button onClick={extractTrendlineFormula}>Start</button>
    </div>
  );
}

export default App;

After running the code, you get the effect of extracting trendline formulas:

Extract Trendline Formula


FAQ

How to delete an added trendline from a chart?

Cause: A trendline has already been added to the chart, but it needs to be removed or replaced.

Solution: Use the TrendLines.RemoveAt(index) method to remove a trendline at a specific index. Indexing starts from 0. For example, chart.Series.get(0).TrendLines.RemoveAt(0) removes the first trendline from the first series.

How to set forward/backward prediction periods for a trendline?

Cause: A trendline can not only fit existing data, but also predict future or past values based on the trend.

Solution: Use the trendLine.Forward and trendLine.Backward properties to set the number of prediction periods forward and backward respectively. For example, trendLine.Forward = 2 predicts two periods beyond the current data, while trendLine.Backward = 1 extrapolates one period before the data.


Get Free License

Spire.XLS for JavaScript offers a 30-day full-featured free trial license with no functional limitations. Apply here to evaluate before purchasing.

Bookmarks in a PDF document organize the document outline as a tree, and they are a core tool for quickly navigating long documents. When a document contains multi-level bookmarks, the default expanded or collapsed state directly affects the outline a reader sees when opening the document. With Spire.PDF for JavaScript, you can reset the expanded and collapsed state of bookmarks in a React application so that the document opens with exactly the outline levels you need.

Spire.PDF for JavaScript processes PDF documents directly in the browser based on WebAssembly, managing input and output files through a virtual file system (VFS), with no backend service required. The key to controlling whether a bookmark is expanded or collapsed is the ExpandBookmark property of the bookmark object: set it to true to expand the node and its child bookmarks, or false to collapse and hide its children. For multi-level bookmarks, you can recursively traverse the PdfBookmarkCollection to set them all at once, or locate a specific node by index to control it individually.

This article covers two core features:

For installation and project configuration, refer to Integrating Spire.PDF for JavaScript in a React Project. The following examples assume Spire.PDF is installed and the WebAssembly module has been initialized.


Expanding All PDF Bookmarks

Spire.PDF for JavaScript can retrieve the bookmark collection of a document through PdfDocument.Bookmarks. Because bookmarks support multiple nesting levels, you need a recursive function that traverses the PdfBookmarkCollection: recursively process the child bookmarks first, then set the ExpandBookmark property of the current node to true, so that every level of bookmarks is fully expanded when the document is opened.

function App() {
  const expandAllBookmarks = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the PDF file to be processed into the VFS
    const inputFileName = 'Sample.pdf';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfDocument object and load the PDF document
    let doc = new pdfModule.PdfDocument();
    doc.LoadFromFile(inputFileName);

    // Recursively traverse the bookmark collection and expand all levels
    function expandBookmarks(collection, expand) {
      // Stop the recursion when the collection is empty
      if (collection.Count === 0) {
        return;
      }

      for (let i = 0; i < collection.Count; i++) {
        let bookmark = collection.get_Item(i);

        // Process the child bookmarks first
        expandBookmarks(bookmark, expand);

        // Then set the expanded state of the current bookmark
        bookmark.ExpandBookmark = expand;
      }
    }

    // Expand all bookmarks in the document
    expandBookmarks(doc.Bookmarks, true);

    // Save the document and trigger the download
    const outputFileName = "ExpandAllBookmarks.pdf";
    doc.SaveToFile(outputFileName);
    doc.Close();

    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/pdf" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Expand All PDF Bookmarks</h1>
      <button onClick={expandAllBookmarks}>
        Start Expanding
      </button>
    </div>
  );
}

export default App;

The document after recursively expanding the bookmarks at every level

The document after recursively expanding the bookmarks at every level


Expanding or Collapsing Specific PDF Bookmarks

If you only need to control a few bookmark nodes, you can use PdfBookmarkCollection.get_Item to locate the target bookmark by index, and then set its ExpandBookmark property individually. Setting the ExpandBookmark of a node to true expands the child bookmarks under it, while setting it to false collapses them, which gives you precise control over a partially expanded, partially collapsed outline.

function App() {
  const toggleSpecificBookmarks = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the PDF file to be processed into the VFS
    const inputFileName = 'Sample.pdf';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfDocument object and load the PDF document
    let doc = new pdfModule.PdfDocument();
    doc.LoadFromFile(inputFileName);

    // Expand the first bookmark (Chapter 1); its child bookmarks are shown as well
    doc.Bookmarks.get_Item(0).ExpandBookmark = true;

    // Collapse the second bookmark (Chapter 2); its child bookmarks are hidden
    doc.Bookmarks.get_Item(1).ExpandBookmark = false;

    // Expand the third bookmark (Chapter 3)
    doc.Bookmarks.get_Item(2).ExpandBookmark = true;

    // Save the document and trigger the download
    const outputFileName = "ToggleSpecificBookmarks.pdf";
    doc.SaveToFile(outputFileName);
    doc.Close();

    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: "application/pdf" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Expand or Collapse Specific PDF Bookmarks</h1>
      <button onClick={toggleSpecificBookmarks}>
        Start Processing
      </button>
    </div>
  );
}

export default App;

The document after expanding or collapsing the specified bookmarks by index

The document after expanding or collapsing the specified bookmarks by index


FAQ

Why do child bookmarks still not appear after setting ExpandBookmark

Reason: ExpandBookmark controls whether the child bookmarks of that node are shown. If the parent node of a bookmark is collapsed, then the bookmark itself will not be displayed no matter how its own ExpandBookmark is set, because its parent is collapsed.

Solution: Expand level by level starting from the root node, or simply set ExpandBookmark to true for the entire bookmark tree with a recursive function:

function expandBookmarks(collection) {
  for (let i = 0; i < collection.Count; i++) {
    let bookmark = collection.get_Item(i);
    bookmark.ExpandBookmark = true;
    expandBookmarks(bookmark);
  }
}

expandBookmarks(doc.Bookmarks);

How to expand only one level of a multi-level bookmark

Reason: Bookmarks form a tree structure, and each node of a PdfBookmarkCollection also exposes its child collection through get_Item, so you need to locate the target level by descending one level at a time.

Solution: Locate the parent node of the target level first, then set the ExpandBookmark of that node. For example, to expand only the first child bookmark under Chapter 2:

// Get the second bookmark (Chapter 2)
let chapterTwo = doc.Bookmarks.get_Item(1);

// Get the first child bookmark under that chapter
let sectionOne = chapterTwo.get_Item(0);

// Expand that child bookmark
sectionOne.ExpandBookmark = true;

Where is the expanded or collapsed state of ExpandBookmark stored

Reason: The expanded or collapsed state of a bookmark is written into the outline (Outlines) structure of the PDF along with the bookmark itself. It is part of the document content, not a temporary setting of the viewer.

Solution: After setting and saving, any viewer that supports the standard PDF outline (such as the bookmark panel of Adobe Acrobat, Edge or the built-in Chrome viewer) will display the state as it was saved. To restore a fully collapsed outline, simply set the ExpandBookmark of the corresponding nodes to false and save again:

// Collapse the first bookmark
doc.Bookmarks.get_Item(0).ExpandBookmark = false;
doc.SaveToFile(outputFileName);

Get a Free License

If you wish to remove the evaluation message from the result document or remove feature limitations, please contact sales to obtain a temporary license valid for 30 days.

page 24