Set the Theme of an Excel Workbook in React with JavaScript

A finished report is often reused for a different brand or a different department, and the colours have to follow. The awkward part is that a workbook holds two kinds of colour. One is a hard-coded RGB value that belongs to the single cell it sits in. The other points at a theme slot: the title bar, the header row, the banded rows and the borders all look different, yet all of them read from the same set of slots. The first kind has to be changed cell by cell, and one missed cell gives the old palette away. The second kind repaints the entire sheet from a single slot. Spire.XLS for JavaScript does this in the browser on top of WebAssembly, managing input and output files through a virtual file system (VFS), with no backend service required.

This article covers three key features:

For installation and project setup, see Integrating Spire.XLS for JavaScript in a React Project. The examples below assume Spire.XLS is installed and the WebAssembly module has been initialised.


Replace an accent colour in the theme

Most reports lean on a single colour: the header row is filled with it, the title bar takes a darker shade, the banded rows a lighter one, and the borders a paler one still. Those shades were not mixed by hand one at a time — they are the same slot read at different tint levels. Reskinning therefore needs no colour picking at all: replace that one slot with the new brand colour and every shade is recomputed, so the whole sheet, chart included, lands on the new palette. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Replace the xlsModule.ThemeColorType.Accent1 slot with workbook.SetThemeColor, giving the new colour through xlsModule.Color.FromArgb.
  4. Save the workbook with workbook.SaveToFile; the title bar, header row, banded rows and borders change together.

The complete code example below shows how to replace an accent colour in the theme in React:

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

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

    // Load the font and the test data into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ThemeSource.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // Replace accent 1 of the theme: the title bar, the header row, the banded rows and the
    // borders all follow it
    workbook.SetThemeColor(
      xlsModule.ThemeColorType.Accent1,
      xlsModule.Color.FromArgb(255, 46, 125, 91),
    );

    // Save the workbook
    const outputFileName = "SetThemeColor.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of the workbook object to release resources
    workbook.Dispose();

    // Read the result file from the VFS and trigger the 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>Set Workbook Theme</h1>
      <button id="set-theme-color" onClick={setThemeColor}>Replace a Theme Colour</button>
    </div>
  );
}

export default App;

After running, replacing an accent colour in the theme:

Replace an accent colour in the theme


Apply a custom colour scheme

Replacing one accent colour unifies the body of the table, but the other series in the chart, the total row and any warning colour stay on the old palette — they read from other slots in the theme. To move a document onto a different scheme outright, change all six accent slots together, so every element that references the theme lands on the new colours at once instead of one changing and a string of others lagging behind. Reading the current values first leaves a baseline to check the result against. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile, then read the R, G and B of the current accent 1 with workbook.GetThemeColor to keep as a baseline.
  3. Put the six slot-and-colour pairs into an array, the colours again built with xlsModule.Color.FromArgb.
  4. Walk the array and call workbook.SetThemeColor on each entry, so all six accent colours change in one pass.
  5. Save the workbook with workbook.SaveToFile.

The complete code example below shows how to apply a custom colour scheme in React:

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

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

    // Load the font and the test data into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ThemeSource.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // Read the current accent colour back first, so it can be compared with the new one
    const before = workbook.GetThemeColor(xlsModule.ThemeColorType.Accent1);
    console.log(`Accent 1 before the change: R=${before.R} G=${before.G} B=${before.B}`);

    // All six accent colours change in one go, so the whole scheme moves together
    const scheme = [
      [xlsModule.ThemeColorType.Accent1, xlsModule.Color.FromArgb(255, 109, 46, 95)],
      [xlsModule.ThemeColorType.Accent2, xlsModule.Color.FromArgb(255, 18, 89, 94)],
      [xlsModule.ThemeColorType.Accent3, xlsModule.Color.FromArgb(255, 138, 106, 22)],
      [xlsModule.ThemeColorType.Accent4, xlsModule.Color.FromArgb(255, 47, 93, 58)],
      [xlsModule.ThemeColorType.Accent5, xlsModule.Color.FromArgb(255, 67, 48, 122)],
      [xlsModule.ThemeColorType.Accent6, xlsModule.Color.FromArgb(255, 138, 59, 46)],
    ];
    for (const [themeColorType, color] of scheme) {
      workbook.SetThemeColor(themeColorType, color);
    }

    // Save the workbook
    const outputFileName = "ApplyThemeScheme.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of the workbook object to release resources
    workbook.Dispose();

    // Read the result file from the VFS and trigger the 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>Set Workbook Theme</h1>
      <button id="apply-theme-scheme" onClick={applyThemeScheme}>Apply a Custom Colour Scheme</button>
    </div>
  );
}

export default App;

After running, applying a custom colour scheme:

Apply a custom colour scheme


Reuse another workbook's theme

Once a colour scheme is signed off it usually already lives in a workbook — the designer's sample, last quarter's report, or the company template. Copying the hex values slot by slot is tedious and easy to get a digit wrong. The theme is itself part of the workbook, so the whole theme can be taken across, carrying both dark and light background pairs and the hyperlink colours with it, and every theme-referenced colour in the target workbook is repainted. The workbook the theme comes from need not match the target's layout at all — how many rows and columns it holds, which data sits in them and which kind of chart it draws make no difference. What crosses over is the theme; the target's own data and chart are left untouched. The steps are:

  1. Load the font and both workbook files into the VFS.
  2. Load the target workbook with workbook.LoadFromFile, then the theme provider with themeWorkbook.LoadFromFile.
  3. Copy the provider's theme across in one piece with workbook.CopyTheme(themeWorkbook).
  4. Save the target workbook with workbook.SaveToFile, then call Dispose on both workbook objects.

The complete code example below shows how to reuse another workbook's theme in React:

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

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

    // Load the font and both workbooks into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ThemeSource.xlsx';
    const themeFileName = 'ThemeAlt.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);
    await window.spire.FetchFileToVFS(themeFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook that is to be reskinned
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });

    // Load the workbook the theme comes from
    const themeWorkbook = new xlsModule.Workbook();
    themeWorkbook.LoadFromFile({ fileName: themeFileName });

    // Copy the theme of the source workbook over in one piece
    workbook.CopyTheme(themeWorkbook);

    // Save the workbook
    const outputFileName = "CopyWorkbookTheme.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of both workbook objects to release resources
    workbook.Dispose();
    themeWorkbook.Dispose();

    // Read the result file from the VFS and trigger the 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>Set Workbook Theme</h1>
      <button id="copy-workbook-theme" onClick={copyWorkbookTheme}>Reuse Another Workbook&apos;s Theme</button>
    </div>
  );
}

export default App;

After running, reusing another workbook's theme:

Reuse another workbook's theme


FAQ

Which colours change with the theme, and which do not

Cause: Colours fall into two kinds. A colour taken from Theme Colors in Excel changes together with the theme; a colour taken from Standard Colors is a fixed value and stays as it is.

Solution: To have a colour change with the theme, select the cell in Excel and pick it from Theme Colors. Colours written in code are all fixed values and do not change with the theme:

const cell = sheet.Range.get('A1');

// A fixed colour: it does not change with the theme
cell.Style.Interior.Color = xlsModule.Color.FromArgb(255, 192, 0, 0);

Can the chart alone be restyled, leaving the table untouched?

Cause: The theme belongs to the whole workbook and makes no distinction between the table and the chart. When the theme changes, every object that takes its colour from the theme changes with it, and the chart is one of them. A chart series holds no colour of its own — it takes the colour from the theme at draw time — so the chart always follows, and one side cannot change on its own.

Solution: To restyle only the chart, leave the theme alone and set the colour on the series directly. That writes a fixed colour into the chart, which the theme does not affect:

const chart = sheet.Charts.get(0);
const serie = chart.Series.get(0);

serie.Format.Fill.FillType = xlsModule.ShapeFillType.SolidColor;
serie.Format.Fill.ForeColor = xlsModule.Color.FromArgb(255, 46, 125, 91);

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.