A cell whose formatting is already right often has to be reused elsewhere: the unit price column carries a money format, a light blue fill and thin borders, while the amount column on the same row is still plain. Copying a cell format hands the look the source cell has already settled on to the target cell -- number format, font, fill, border and alignment in one go -- and leaves the data in the target cell untouched. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; the input and output files are managed by a virtual file system (VFS), with no backend service required.

This article covers two feature points:

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 initialized.


Copy the whole cell format

Every part of a cell's look is kept in a style object of its own. Handing the source cell's style object straight to the target cell carries the number format, font, fill, border and alignment across in one go, while the data in the target cell stays where it is. The sample workbook's unit price column already carries a money format and a light blue fill, and the amount column is still plain, so copying the former onto the latter row by row shows the difference.

Assigning the source cell's whole Style to the target cell's Style completes the copy:

Syntax Result
sheet.Range.get('C2').Style Takes the whole style object of the source cell (the unit price column)
sheet.Range.get('E2').Style = sheet.Range.get('C2').Style Hands that whole style to the target cell (the amount column): number format, font, fill, border and alignment follow
sheet.Range.get({ row, column: 5 }).Text The value already in the target cell stays where it is, unaffected by the style assignment

The steps are:

  1. Load the font file and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), load the input file with workbook.LoadFromFile, and take the first worksheet with workbook.Worksheets.get(0).
  3. Take the source column's cell and the target column's cell row by row with sheet.Range.get.
  4. Assign the source cell's Style to the target cell's Style; the whole cell format follows, and the target cell's value is left untouched.
  5. Save the workbook with workbook.SaveToFile and dispose of it with workbook.Dispose to free resources.

The full code example below shows how to copy the whole cell format in React:

function App() {
  const copyCellStyle = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'CopyCellStyle.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the workbook and take the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // row by row, put the unit price column's format onto the amount column; the amounts stay put
    for (let row = 2; row <= 7; row++) {
      sheet.Range.get(`E${row}`).Style = sheet.Range.get(`C${row}`).Style;
    }

    // save the workbook
    const outputFileName = 'CopyCellStyle-output.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Copy Cell Format</h1>
      <button id="copy-cell-style" onClick={copyCellStyle}>Copy the Whole Cell Format</button>
    </div>
  );
}

export default App;

The worksheet after copying the whole cell format:

The whole cell format copied


Clone and reuse a named style

When one set of formatting is needed in several places, registering it as a named style lets it be applied to any range by name, and a colour change has to be made in one place only. A named style lives on the workbook: create it with workbook.Styles.Add, set its properties, then apply it by name through Range.CellStyleName. The example builds a style of white text on a steel blue fill for the header row, then clones it and changes only the colours for the total line -- the change lands on the clone alone, and the original style is unaffected.

A named style is registered on the workbook and applied to a range by name:

Syntax Result
workbook.Styles.Add('OrderHeader') Registers a named style called OrderHeader on the workbook
headerStyle.Font.Color, headerStyle.Color, headerStyle.FillPattern Sets the text colour, the fill colour and the fill pattern one by one
headerStyle.clone() Copies out a style of its own, with a number appended to the name
sheet.Range.get('A1:E1').CellStyleName = headerStyle.Name Applies the named style to that range by name

The steps are:

  1. Load the font file and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), load the input file with workbook.LoadFromFile, and take the first worksheet with workbook.Worksheets.get(0).
  3. Create a named style with workbook.Styles.Add and set its font name, size, bold, text colour, fill colour, fill pattern and horizontal alignment.
  4. Apply the style to the header range by name with sheet.Range.get('A1:E1').CellStyleName.
  5. Clone it with the style's clone(), change only the text colour and the fill colour, and get the variant the total line needs.
  6. Apply the variant to the total line with sheet.Range.get('A8:E8').CellStyleName.
  7. Save the workbook with workbook.SaveToFile and dispose of it with workbook.Dispose to free resources.

The full code example below shows how to clone a named style and reuse it for different ranges in React:

function App() {
  const cloneNamedStyle = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'CopyCellStyle.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the workbook and take the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // create a named style: white text on a steel blue fill, centred
    const headerStyle = workbook.Styles.Add('OrderHeader');
    headerStyle.Font.FontName = 'Arial';
    headerStyle.Font.Size = 11;
    headerStyle.Font.IsBold = true;
    headerStyle.Font.Color = xlsModule.Color.get_White();
    headerStyle.Color = xlsModule.Color.get_SteelBlue();
    headerStyle.FillPattern = xlsModule.ExcelPatternType.Solid;
    headerStyle.HorizontalAlignment = xlsModule.HorizontalAlignType.Center;

    // apply the named style to the header row
    sheet.Range.get('A1:E1').CellStyleName = headerStyle.Name;

    // clone it and change only the colours, giving the variant the total line needs
    const totalStyle = headerStyle.clone();
    totalStyle.Font.Color = xlsModule.Color.get_DarkBlue();
    totalStyle.Color = xlsModule.Color.get_LightYellow();

    // apply the cloned style to the total line
    sheet.Range.get('A8:E8').CellStyleName = totalStyle.Name;

    // save the workbook
    const outputFileName = 'CloneNamedStyle.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Clone and Reuse a Named Style</h1>
      <button id="clone-named-style" onClick={cloneNamedStyle}>Clone and Reuse a Named Style</button>
    </div>
  );
}

export default App;

The worksheet after cloning a named style and reusing it:

A named style cloned and reused


FAQ

Copying the number format alone, without the fill or the border

Reason: A whole-cell copy takes the fill, font and border along with the number format, which is more than a change of display calls for.

Solution: Take NumberFormat from the source cell's style and assign that single property to the target cell's style, leaving the rest untouched:

// take the number format of the total line's quantity cell as the format to apply
const sourceStyle = sheet.Range.get('D8').Style;

// put only the number format onto the quantity column; the bold and the top border stay untouched
for (let row = 2; row <= 7; row++) {
  sheet.Range.get(`D${row}`).Style.NumberFormat = sourceStyle.NumberFormat;
}

Why a whole font object cannot be assigned to the target cell

Reason: Style.Font is a read-only property; it returns the font object the cell currently carries, so it cannot be replaced wholesale -- only the members hanging off it can be changed.

Solution: Set FontName, Size, Color, IsBold and the like one at a time. To bring the fill and the border across as well, assign the whole Style; to reuse a group of settings, register them as a named style and apply that style by name.

Does re-colouring a clone change the original style as well

Cause: clone() produces a copy that stands on its own, and once saved it takes a separate entry among the workbook's named styles; the name has a number appended to keep the two apart, so OrderHeader clones to OrderHeader1. Changing the text colour and the fill colour on the copy leaves the original where it was, still carrying the colours it had before the clone.

Solution: When several colour variants are needed, clone them all from the same base style and adjust each one separately; the definition of the base style never moves.

A named style or a plain Style assignment -- which one to use

Cause: Assigning a Style copies the source cell's current look onto the target cell in one go, which suits a one-off move; Range.CellStyleName applies a named style registered on the workbook, so a format used over and over is referred to by a single name in the code.

Solution: For a one-off move of the look, assign the whole Style; for a format reused in several places, build a named style with workbook.Styles.Add and apply it by name.


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.

When a workbook has to be handed to a front-end program, JSON is the shape that fits APIs, caches and components alike: one array per worksheet, one object per row, and the column headers as the keys. Once the workbook is converted, the table data joins the data flow of the page without a backend parsing it first. Spire.XLS for JavaScript performs the conversion in the browser on WebAssembly, handling input and output files through a virtual file system (VFS) with no backend service.

This article covers four core feature points:

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.


Convert the Entire Workbook to JSON

Every worksheet is a set of records that share a shape: the first row names the fields, and each row below it is one record. Handing over the whole workbook writes all of them at once, with the worksheet name as the key and the records as an array under it, so there is no need to call the conversion once per worksheet or to pick a range in advance. The sample workbook's three worksheets, the sales ledger, the customers and the notes, are written together, one top-level key each.

The whole-workbook conversion only has to name the target format when saving:

Syntax Result
fileFormat: xlsModule.FileFormat.Json Writes the workbook as JSON, one top-level key per worksheet

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), then load the input file with workbook.LoadFromFile.
  3. Save the workbook with workbook.SaveToFile, naming xlsModule.FileFormat.Json in fileFormat.
  4. Dispose of the workbook with workbook.Dispose to free resources.

The code below is the complete example of converting the entire workbook to JSON:

function App() {
  const workbookToJson = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'Sample.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // save the whole workbook as JSON
    const outputFileName = 'WorkbookToJson.json';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Json });

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

    // read the result back out of the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/json' });
    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>Convert Excel to JSON</h1>
      <button id="workbook-to-json" onClick={workbookToJson}>Convert the Entire Workbook to JSON</button>
    </div>
  );
}

export default App;

The JSON file after converting the entire workbook:

The JSON file after converting the entire workbook


Convert a Specific Worksheet to JSON

Most of the time only one worksheet is meant for the front end, while the rest are internal ledgers or instructions. Writing them all out lengthens the response and leaves the caller to pick the right one. The way around it is to copy the worksheet into a new workbook that holds nothing else and save that, so the result carries this one worksheet only. In the example the third worksheet, the notes, is exported, and the sales ledger and customers worksheets stay out of the result.

Isolating one worksheet comes down to three operations on the Worksheets collection:

Syntax Result
workbook.Worksheets.get(2) Takes the third worksheet by index, counting from 0
workbook.Worksheets.Clear() Clears the worksheets the workbook already holds
workbook.Worksheets.AddCopy({ sheet }) Copies the whole source worksheet into the cleared workbook

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create the source workbook with new Workbook() and load the input file with workbook.LoadFromFile, then take the worksheet to export with Worksheets.get(2).
  3. Create a second workbook with new Workbook() and clear the three blank worksheets it starts with using Worksheets.Clear().
  4. Copy the target worksheet in with Worksheets.AddCopy({ sheet }), so that it is the only one left.
  5. Save the new workbook with SaveToFile, naming xlsModule.FileFormat.Json in fileFormat.
  6. Dispose of both workbook objects with Dispose.

The code below is the complete example of converting a specific worksheet to JSON:

function App() {
  const worksheetToJson = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'Sample.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the source workbook and take the third worksheet
    const sourceWorkbook = new xlsModule.Workbook();
    sourceWorkbook.LoadFromFile({ fileName: inputFileName });
    const targetSheet = sourceWorkbook.Worksheets.get(2);

    // create a new workbook and clear the three worksheets it starts with
    const targetWorkbook = new xlsModule.Workbook();
    targetWorkbook.Worksheets.Clear();

    // copy the target worksheet in, so that it is the only one left
    targetWorkbook.Worksheets.AddCopy({ sheet: targetSheet });

    // save that worksheet alone
    const outputFileName = 'WorksheetToJson.json';
    targetWorkbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Json });

    // dispose of the workbook to release its resources
    sourceWorkbook.Dispose();
    targetWorkbook.Dispose();

    // read the result back out of the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/json' });
    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>Convert Excel to JSON</h1>
      <button id="worksheet-to-json" onClick={worksheetToJson}>Convert a Specific Worksheet to JSON</button>
    </div>
  );
}

export default App;

The JSON file after converting a specific worksheet:

The JSON file after converting a specific worksheet


Convert a Specific Cell Range to JSON

A worksheet usually carries more than the records themselves: title rows, totals formulas, helper columns pasted in from somewhere else. Exporting the whole sheet takes all of that along, when the caller only wants the tidy block in the middle. Naming the range first keeps everything outside it out of the result. The example takes A1:D6 of the sales ledger, six rows by four columns, leaving the price, quantity and amount columns behind in the workbook.

A range export follows the same path as a worksheet export, with a block of cells standing in for the whole sheet:

Syntax Result
sourceSheet.Range.get('A1:D6') Takes the source cell range by address
workbook.Worksheets.Add('RangeData') Adds a worksheet called RangeData to the cleared workbook
targetSheet.Range.get('A1:D6') Takes a destination range of the same size as the source
sourceSheet.Copy({ sourceRange, destRange, copyStyle: true }) Copies the values and styles into the destination range; copyStyle set to true brings the styles along

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create the source workbook with new Workbook() and load the input file with workbook.LoadFromFile, then take the source worksheet with Worksheets.get(0).
  3. Mark out the cell range to export with sourceSheet.Range.get('A1:D6').
  4. Create a target workbook and clear its default worksheets with Worksheets.Clear(), then add a worksheet to receive the range with Worksheets.Add('RangeData').
  5. Take a destination range of the same size with targetSheet.Range.get('A1:D6') and copy the values and styles across with sourceSheet.Copy({ sourceRange, destRange, copyStyle: true }).
  6. Save the new workbook with SaveToFile, naming xlsModule.FileFormat.Json in fileFormat, and dispose of both workbook objects.

The code below is the complete example of converting a specific cell range to JSON:

function App() {
  const rangeToJson = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'Sample.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the source workbook and take the first worksheet
    const sourceWorkbook = new xlsModule.Workbook();
    sourceWorkbook.LoadFromFile({ fileName: inputFileName });
    const sourceSheet = sourceWorkbook.Worksheets.get(0);

    // name the cell range to export
    const sourceRange = sourceSheet.Range.get('A1:D6');

    // create a new workbook, clear its default worksheets and add one to receive the range
    const targetWorkbook = new xlsModule.Workbook();
    targetWorkbook.Worksheets.Clear();
    const targetSheet = targetWorkbook.Worksheets.Add('RangeData');

    // copy the values and styles across; the destination range must match the source size
    const destRange = targetSheet.Range.get('A1:D6');
    sourceSheet.Copy({ sourceRange, destRange, copyStyle: true });

    // save the new workbook
    const outputFileName = 'RangeToJson.json';
    targetWorkbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Json });

    // dispose of the workbook to release its resources
    sourceWorkbook.Dispose();
    targetWorkbook.Dispose();

    // read the result back out of the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/json' });
    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>Convert Excel to JSON</h1>
      <button id="range-to-json" onClick={rangeToJson}>Convert a Specific Cell Range to JSON</button>
    </div>
  );
}

export default App;

The JSON file after converting a specific cell range:

The JSON file after converting a specific cell range


Customize the JSON Output Format

The three conversions above all produce a fixed shape: the field names are taken from the header row as they stand, every value is written as a string, an amount comes out as "186400", a boolean as "TRUE", a date in the runtime's own format rather than as yyyy-MM-dd, and a blank cell leaves its field out altogether. A result like that still has to be cleaned up before an API or a component binding can use it. Reading the cells one by one instead lets you decide the field names, the type of every value, and how a blank is represented.

Reading the cells yourself calls for the getters that return real types:

Syntax Result
sheet.get(row, column) Takes a cell by row and column number, both counting from 1
sheet.LastRow / sheet.LastColumn The last row and column of the used range
cell.Text The text in the cell; a blank cell returns an empty string
cell.NumberValue The number in a numeric column, as a number; a blank cell returns null
cell.BooleanValue The true or false of a boolean column, as a boolean
cell.DateTimeValue The Date object of a date column, ready to be formatted
JSON.stringify(records, null, 2) Serialises the records into JSON text indented by two spaces

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS, load the workbook with new Workbook() and workbook.LoadFromFile, and take the customers worksheet with Worksheets.get(1).
  2. List the fields to keep in an array, each entry naming its column header, its output field name and how its value is read.
  3. Read the header row with sheet.get(1, column) and, using sheet.LastColumn, map each column header to its column number.
  4. Walk the rows from row 2 to sheet.LastRow, reading each value with cell.Text, cell.NumberValue, cell.BooleanValue or cell.DateTimeValue according to its field, and recording a blank cell as null.
  5. Build a yyyy-MM-dd date from the year, month and day of the Date object.
  6. Serialise the records with JSON.stringify and a two-space indent, then write the text into the VFS with FS.writeFile.
  7. Dispose of the workbook with workbook.Dispose to free resources.

The code below is the complete example of customizing the JSON output format:

function App() {
  const customJsonFormat = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'Sample.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the workbook and take the customers worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(1);

    // the fields to keep: column header, output field name, how to read the value
    const fields = [
      { header: 'Customer ID', key: 'customerId', type: 'text' },
      { header: 'Customer Name', key: 'customerName', type: 'text' },
      { header: 'City', key: 'city', type: 'text' },
      { header: 'First Order Date', key: 'firstOrderDate', type: 'date' },
      { header: 'Renewed', key: 'renewed', type: 'boolean' },
      { header: 'Total Amount (CNY)', key: 'totalAmount', type: 'number' },
      { header: 'Remark', key: 'remark', type: 'text' },
    ];

    // read the header row and map each column header to its column number
    const columnOf = {};
    for (let column = 1; column <= sheet.LastColumn; column++) {
      columnOf[sheet.get(1, column).Text] = column;
    }

    // dates all go out as yyyy-MM-dd
    const pad = (value) => String(value).padStart(2, '0');
    const formatDate = (date) =>
      `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;

    // walk the rows; a blank cell becomes null
    const records = [];
    for (let row = 2; row <= sheet.LastRow; row++) {
      const record = {};
      for (const field of fields) {
        const cell = sheet.get(row, columnOf[field.header]);
        if (field.type === 'number') {
          record[field.key] = cell.NumberValue;
        } else if (field.type === 'boolean') {
          record[field.key] = cell.BooleanValue;
        } else if (field.type === 'date') {
          record[field.key] = formatDate(cell.DateTimeValue);
        } else {
          const text = cell.Text;
          record[field.key] = text === '' ? null : text;
        }
      }
      records.push(record);
    }

    // write with a two-space indent so the result stays readable
    const json = JSON.stringify(records, null, 2);

    // write the JSON string into the VFS
    const outputFileName = 'CustomJsonFormat.json';
    window.dotnetRuntime.Module.FS.writeFile(outputFileName, new TextEncoder().encode(json));

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

    // read the result back out of the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/json' });
    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>Convert Excel to JSON</h1>
      <button id="custom-json-format" onClick={customJsonFormat}>Customize the JSON Output Format</button>
    </div>
  );
}

export default App;

The JSON file after applying the custom output format:

The JSON file after applying the custom output format


FAQ

Why are numbers, booleans and dates all strings in the exported JSON

Cause: SaveToFile writes the stored value of each cell. It neither applies the cell's number format nor infers a type, so an amount is written as "186400", a boolean as "TRUE", and a date in the runtime's own format instead of yyyy-MM-dd.

Solution: To get real types, read the cells yourself and take the value with cell.NumberValue, cell.BooleanValue or cell.DateTimeValue, then let JSON.stringify serialise the result.

Why do blank cells not appear in the JSON

Cause: Both the workbook and the worksheet conversion use the header row as the field names, and a blank cell produces no field at all, so that object ends up with fewer keys than the others.

Solution: If the caller needs every object to carry the same keys, read the cells yourself and write null whenever cell.Text comes back as an empty string.

Why is there an extra Evaluation Warning key at the top level of the JSON

Cause: The trial build adds an evaluation warning worksheet to the workbook, and in a whole-workbook conversion it becomes one more top-level key alongside the real worksheets.

Solution: Convert a specific worksheet or a specific cell range instead, and only the data you asked for is written; if the whole workbook has to be exported as it stands, contact sales for a temporary license that removes the evaluation message.

How do I export only a few columns

Cause: The workbook and worksheet conversions both write every column in the worksheet, while a range export can only take a consecutive block, so columns such as A, C and E cannot be expressed as a range.

Solution: Read the cells yourself the way the custom output format does and keep the fields you need; when the columns happen to be consecutive, a range export narrows the result just as well.


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.

A single column often packs several pieces of information into one cell: a contact's name, phone number and city written on one line, an order code joining the country code, the date and a serial number into one string. Such data can neither be sorted nor filtered by any one of those parts. Splitting text into columns takes a column of text apart by a given rule and drops the pieces into the columns to its right, leaving the original data where it is. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; the input and output files are managed by a virtual file system (VFS), with no backend service required.

This article covers two feature points:

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 initialized.


Split on a separator

When every entry on a line is separated from the next by the same character, splitting on that character moves them into the columns to the right. Each kind of information then has a column of its own, ready for sorting, checking or grouping, while the original data stays where it is.

The split reads the text out of the first column, cuts it apart, and writes the pieces back into the columns on the right:

Syntax Result
sheet.Range.get({ row, column: 1 }).Text Reads the raw text of the first column on that row
.split(' ') Cuts the text apart on the space
sheet.Range.get({ row, column: index + 2 }).Text = piece Writes the piece at index into the index + 1 column to its right
sheet.AllocatedRange.AutoFitColumns() Sizes every column to its content

The steps are:

  1. Load the font file and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), load the input file with workbook.LoadFromFile, and take the target worksheet with workbook.Worksheets.get(0).
  3. Take the last data row with sheet.LastRow and work row by row from row 2, skipping the header.
  4. Read the raw text of the first column with sheet.Range.get({ row, column: 1 }).Text and split it on the separator with split(' ').
  5. Walk the pieces and write them back from the second column on with sheet.Range.get({ row, column: index + 2 }).Text.
  6. Size every column to its content with sheet.AllocatedRange.AutoFitColumns().
  7. Save the workbook with workbook.SaveToFile and dispose of it with workbook.Dispose to free resources.

The full code example below shows how to split an Excel column on a separator in React:

function App() {
  const splitByDelimiter = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SplitTextIntoColumns.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the workbook and take the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // row by row, take the text in column A, split it on the space and write the pieces into the columns on the right
    const lastRow = sheet.LastRow;
    for (let row = 2; row <= lastRow; row++) {
      const pieces = sheet.Range.get({ row, column: 1 }).Text.split(' ');
      pieces.forEach((piece, index) => {
        sheet.Range.get({ row, column: index + 2 }).Text = piece;
      });
    }

    // size every column to its content
    sheet.AllocatedRange.AutoFitColumns();

    // save the workbook
    const outputFileName = 'SplitByDelimiter.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Split Excel Data into Columns</h1>
      <button id="split-by-delimiter" onClick={splitByDelimiter}>Split by a Separator</button>
    </div>
  );
}

export default App;

The worksheet after splitting on a separator:

Splitting on a separator


Split by fixed width

Other text is not divided by any character at all: every entry has a fixed length and they run end to end, as a code assembled from fields of known width. Such text is cut by the number of characters in each entry, carrying on from where the previous one ended.

The cut advances through the source text by the length of each entry and writes the pieces back into the columns on the right:

Syntax Result
sheet.Range.get({ row, column: 1 }).Text Reads the raw code of the first column on that row
code.substring(start, start + width) Cuts out width characters starting at start
start += width Moves the cutting position past that entry, ready for the next
sheet.Range.get({ row, column: index + 2 }).Text = piece Writes the piece at index into the index + 1 column to its right
sheet.AllocatedRange.AutoFitColumns() Sizes every column to its content

The steps are:

  1. Load the font file and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), load the input file with workbook.LoadFromFile, and take the worksheet holding the text with workbook.Worksheets.get(2).
  3. Note down the number of characters in each entry in an array; the lengths should add up to that of the source text.
  4. Take the last data row with sheet.LastRow and work row by row from row 2, skipping the header.
  5. Cut one entry out with code.substring(start, start + width), move the starting position on by that length, and write it back from the second column on with sheet.Range.get({ row, column: index + 2 }).Text.
  6. Size every column to its content with sheet.AllocatedRange.AutoFitColumns().
  7. Save the workbook with workbook.SaveToFile and dispose of it with workbook.Dispose to free resources.

The full code example below shows how to split an Excel column by fixed width in React:

function App() {
  const splitByFixedWidth = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SplitTextIntoColumns.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // load the workbook and take the third worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(2);

    // row by row, take the code in column A and cut it at the fixed lengths into the columns on the right
    const widths = [2, 4, 2, 2, 3];
    const lastRow = sheet.LastRow;
    for (let row = 2; row <= lastRow; row++) {
      const code = sheet.Range.get({ row, column: 1 }).Text;
      let start = 0;
      widths.forEach((width, index) => {
        sheet.Range.get({ row, column: index + 2 }).Text = code.substring(start, start + width);
        start += width;
      });
    }

    // size every column to its content
    sheet.AllocatedRange.AutoFitColumns();

    // save the workbook
    const outputFileName = 'SplitByFixedWidth.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Split Excel Data into Columns</h1>
      <button id="split-by-fixed-width" onClick={splitByFixedWidth}>Split by Fixed Width</button>
    </div>
  );
}

export default App;

The worksheet after splitting by fixed width:

Splitting by fixed width


FAQ

The column uses more than one kind of separator, how can it be split in one pass

Reason: split(' ') cuts on a single character at a time; when a text mixes commas, slashes and the like, each separator has to be handled on its own, so the code keeps piling up and one of them is easily left out.

Solution: Put the separators you expect into a character class, and a single split covers them all -- the characters inside the brackets are alternatives, so any one of them counts as a split point. The sample workbook's address sheet separates the levels of an address with a comma or a slash, and this form splits it into three columns at once:

// a comma and a slash both count as split points, taken apart in one pass
const pieces = sheet.Range.get({ row, column: 1 }).Text.split(/[,/]/);

An empty column turns up in the middle of the split result

Reason: split(' ') cuts strictly on a single space, so two adjacent spaces in the raw text cut out an empty string between them, and that empty string lands on the sheet as a blank column.

Solution: Strip the leading and trailing spaces with trim() first, then split on /\s+/ -- a run of consecutive spaces counts as one split point, and no empty string is cut out in between:

// strip the surrounding spaces, then split on a run of whitespace so no empty piece is cut out
const pieces = sheet.Range.get({ row, column: 1 }).Text.trim().split(/\s+/);

Does splitting into columns overwrite what the columns on the right already hold

Cause: The pieces are written cell by cell with sheet.Range.get({ row, column: index + 2 }).Text = piece, so whatever the target column held on that row is replaced by the entry written into it. The text in the first column is untouched; on the right, the columns the pieces land in are overwritten, and any column beyond them keeps its old data.

Solution: To keep what is already on the right, write the result further out into empty columns, or into a separate worksheet instead; sheet.LastColumn tells you whether the right-hand side already holds anything before you write.

What happens when the entry lengths do not add up to the length of the source text

Cause: substring(start, start + width) only takes what the source text actually holds. When the lengths add up to less than the source text, the characters left at the end of it go into no column at all; when they add up to more, the overrun is cut to an empty string and leaves a blank column.

Solution: Check that the lengths add up to the length of the source text before cutting, and every entry lands in a column of its own.


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.

A workbook is made for editing, which does not always make it the right thing to hand over for archiving or printing. XPS, OFD and PostScript are all fixed-layout documents: the page size, the position of every piece of text and the fonts are pinned when the file is written, so the document looks the same on any machine that opens it. Spire.XLS for JavaScript performs all three conversions in the browser on WebAssembly, handling input and output files through a virtual file system (VFS) with no backend service.

This article covers three core feature points:

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.


Convert Excel to XPS

XPS is a fixed-layout document format that ships with Windows, and saving a workbook as XPS gives it an archived copy that no longer rearranges itself to suit the program that opens it, which is what makes it fit for a report that has to be read on a machine without Excel installed. The conversion changes only the format the file is saved in: the page setup, the cell formatting and the fonts of the worksheet are all written out with it, and a fileFormat argument names the target.

The fileFormat argument of workbook.SaveToFile decides which of the formats is written:

Syntax Result
workbook.LoadFromFile({ fileName: inputFileName }) Loads the workbook to convert from the VFS
workbook.SaveToFile({ fileName: 'ExcelToXPS.xps', fileFormat: xlsModule.FileFormat.XPS }) Writes the workbook as XPS, pinning the page size, the text position and the fonts
workbook.Dispose() Releases the resources held by the workbook object

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), then load the Excel file with workbook.LoadFromFile.
  3. Save the workbook with workbook.SaveToFile, naming xlsModule.FileFormat.XPS in fileFormat.
  4. Dispose of the workbook with workbook.Dispose to free resources.

The code below is the complete example of converting a workbook to XPS in React:

function App() {
  const excelToXps = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SalesReport.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // save as XPS, naming the format with fileFormat
    const outputFileName = 'ExcelToXPS.xps';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.XPS });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/vnd.ms-xpsdocument' });
    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>Convert Excel to XPS, OFD or PostScript</h1>
      <button id="excel-to-xps" onClick={excelToXps}>Convert Excel to XPS</button>
    </div>
  );
}

export default App;

The document after converting Excel to XPS:

Convert an Excel workbook to an XPS document


Convert Excel to OFD

OFD is the fixed-layout document format used by Chinese electronic official documents and e-invoices, describing a page in a national standard so that it can be kept for the long term. Saving a sales report as OFD hands other systems a page whose layout is already fixed, and it no longer matters whether that system has the fonts or applies the same row heights. The conversion runs exactly like the previous feature point, with OFD named as the format.

Syntax Result
workbook.LoadFromFile({ fileName: inputFileName }) Loads the workbook to convert from the VFS
workbook.SaveToFile({ fileName: 'ExcelToOFD.ofd', fileFormat: xlsModule.FileFormat.OFD }) Writes the workbook as OFD, describing the page in the national standard
workbook.Dispose() Releases the resources held by the workbook object

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), then load the Excel file with workbook.LoadFromFile.
  3. Save the workbook with workbook.SaveToFile, naming xlsModule.FileFormat.OFD in fileFormat.
  4. Dispose of the workbook with workbook.Dispose to free resources.

The code below is the complete example of converting a workbook to OFD in React:

function App() {
  const excelToOfd = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SalesReport.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // save as OFD, naming the format with fileFormat
    const outputFileName = 'ExcelToOFD.ofd';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.OFD });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/ofd' });
    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>Convert Excel to XPS, OFD or PostScript</h1>
      <button id="excel-to-ofd" onClick={excelToOfd}>Convert Excel to OFD</button>
    </div>
  );
}

export default App;

The document after converting Excel to OFD:

Convert an Excel workbook to an OFD document


Convert Excel to PostScript

PostScript is the page description language used by printing devices and print workflows. The file is readable text that carries both the page description and the fonts embedded in it, so it can go straight to a PostScript printer or to typesetting software. When a report is headed for a print run, handing over PostScript is safer than handing over the original workbook. The usage matches the first two feature points, with PostScript named as the format.

Syntax Result
workbook.LoadFromFile({ fileName: inputFileName }) Loads the workbook to convert from the VFS
workbook.SaveToFile({ fileName: 'ExcelToPostScript.ps', fileFormat: xlsModule.FileFormat.PostScript }) Writes the workbook as PostScript, a readable page description
workbook.Dispose() Releases the resources held by the workbook object

The steps are:

  1. Load the font and the test data file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook(), then load the Excel file with workbook.LoadFromFile.
  3. Save the workbook with workbook.SaveToFile, naming xlsModule.FileFormat.PostScript in fileFormat.
  4. Dispose of the workbook with workbook.Dispose to free resources.

The code below is the complete example of converting a workbook to PostScript in React:

function App() {
  const excelToPostScript = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SalesReport.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // save as PostScript, naming the format with fileFormat
    const outputFileName = 'ExcelToPostScript.ps';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.PostScript });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/postscript' });
    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>Convert Excel to XPS, OFD or PostScript</h1>
      <button id="excel-to-ps" onClick={excelToPostScript}>Convert Excel to PostScript</button>
    </div>
  );
}

export default App;

The document after converting Excel to PostScript:

Convert an Excel workbook to a PostScript document


FAQ

Why does the conversion fail with Cannot found font?

Cause: Writing XPS, OFD and PostScript means embedding every font the document uses, so a workbook whose text is not in the default Latin range needs the font file for it. If the font was never loaded into the VFS before the conversion, the module cannot find it and SaveToFile stops with a Cannot found font(...) installed on the system. error instead of writing a document.

Solution: Put the font file in public/font and load it into the VFS before loading the workbook, using the same file name the code refers to:

await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);

Does the worksheet's page setup affect the converted document?

Cause: XPS, OFD and PostScript are all written page by page, and the size and the orientation of that page come from the worksheet's own page setup rather than from a decision the conversion makes on its own.

Solution: They carry over as they are. Taking the report in this example, the XPS page box is 794 x 1122 (portrait) while the report keeps its default orientation; set the first worksheet's sheet.PageSetup.Orientation to xlsModule.PageOrientationType.Landscape before converting, and the page box becomes 1122 x 794 (landscape). Paper size, margins and print area take effect on the way out in the same manner.

Why is the converted file much larger than the original workbook

Cause: Writing XPS, OFD and PostScript embeds every font the workbook uses, so the result comes out several times the size of a workbook that only holds cell data. That embedding is what lets a fixed-layout document be opened away from the machine it was made on.

Solution: This is normal and nothing needs fixing. To bring the size down, use a smaller font file, or check first that the report does not rely on several font files at once.


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.

Formatting a cell range cell by cell means repeating the same group of properties down every column, and with enough columns one of them is easily missed; these formats also tend to be spread across fonts, borders and alignment, so each has to be set and then checked again on its own. Spire.XLS stores that group of formats as a named style, uses a flag object to state which of those formats should land this time, and then applies it to the target range: properties that were not ticked are not applied, and every other format on the range is left unchanged. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; the output file is managed by a virtual file system (VFS), with no backend service required.

This article covers two feature points:

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 initialized.


Apply a named style to a cell range

Setting formats cell by cell means repeating the same block of code in every column, and with enough columns a cell is easily missed; fonts, borders and centring set separately would also mean working over the same block three times over. A named style stores a group of formats as an object that can be taken up repeatedly, and the flag object states which of those formats should land this time; together they apply the wanted parts to the whole range in a single call, leaving the range's other formatting unchanged. The steps are:

  1. Load the font file into the VFS, create a workbook, take the first worksheet and write the header and two data rows.
  2. Create a named style with workbook.Styles.Add and give it the font colour, size and italic the header will use.
  3. Set the top and bottom borders on that same style.
  4. Set horizontal and vertical centring on that same style.
  5. Create the flag object with new xlsModule.CellStyleFlag() and tick the properties to land this time; the items left unticked are not written into the target cells.
  6. Apply the style to the header range with sheet.Range.get('A1:E1').ApplyStyle(tableStyle, flag); set the column widths so the header text displays in full, then save the workbook and dispose of it to free resources.

The full code example below shows how to apply cell styles to an Excel cell range in React:

function App() {
  const applyStyleToRange = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);

    // create a new workbook and take the first worksheet
    const workbook = new xlsModule.Workbook();
    const sheet = workbook.Worksheets.get(0);

    // write the header and two data rows
    const columns = ['A', 'B', 'C', 'D', 'E'];
    const headers = ['Product', 'Spec', 'Quantity', 'Unit price', 'Amount'];
    headers.forEach((text, index) => {
      sheet.Range.get(`${columns[index]}1`).Value = text;
    });

    const rows = [
      ['Blu-ray mini projector', '1080P / 8G', '6', '1299', '7794'],
      ['Portable Bluetooth speaker', '20W / IPX7', '12', '399', '4788'],
    ];
    rows.forEach((row, rowIndex) => {
      columns.forEach((column, columnIndex) => {
        sheet.Range.get(`${column}${rowIndex + 2}`).Value = row[columnIndex];
      });
    });

    // create a named style
    const tableStyle = workbook.Styles.Add('tableStyle');

    // give the header its font colour, size and italic
    tableStyle.Font.Color = xlsModule.Color.get_CornflowerBlue();
    tableStyle.Font.Size = 16;
    tableStyle.Font.IsItalic = true;

    // set the top and bottom borders on the same style
    tableStyle.Borders.get(xlsModule.BordersLineType.EdgeTop).LineStyle = xlsModule.LineStyleType.Thin;
    tableStyle.Borders.get(xlsModule.BordersLineType.EdgeBottom).LineStyle = xlsModule.LineStyleType.Thin;

    // and set horizontal and vertical centring on the same style
    tableStyle.HorizontalAlignment = xlsModule.HorizontalAlignType.Center;
    tableStyle.VerticalAlignment = xlsModule.VerticalAlignType.Center;

    // create the flag object and tick the properties to land this time; unticked ones are not written to the target cells
    const flag = new xlsModule.CellStyleFlag();
    flag.FontColor = true;
    flag.FontSize = true;
    flag.FontItalic = true;
    flag.Borders = true;
    flag.HorizontalAlignment = true;
    flag.VerticalAlignment = true;

    // apply the style to the header range
    sheet.Range.get('A1:E1').ApplyStyle(tableStyle, flag);

    // set the column widths
    sheet.Columns.get(0).ColumnWidth = 22;
    sheet.Columns.get(1).ColumnWidth = 14;

    // save the workbook as an Excel file
    const outputFileName = 'ApplyStyleToRange.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Apply cell styles to a cell range</h1>
      <button id="apply-style-to-range" onClick={applyStyleToRange}>Apply cell styles to the cell range</button>
    </div>
  );
}

export default App;

After running, the effect of applying the named style to the cell range:

Apply cell styles to a cell range


Read and adjust the named style

Once a style has been saved, later changes concern the values inside it rather than the whole set of formats, and no workbook has to be rebuilt: load the result file produced by feature point 1, read the named style by name, take the size already set in it, increase it, and apply the style to another range. The flag decides the scope here too: this time only the size, borders and centring are ticked, so the style's colour and italic are not written into the data range. Note that applying a style writes its properties into the cells as they stand at that moment, and the cells keep no reference to the style, so the range has to be applied again for the change to take effect. The steps are:

  1. Run the code of feature point 1 first, so that its result file ApplyStyleToRange.xlsx stays in the VFS as this run's input file.
  2. Load that input file with workbook.LoadFromFile and take the first worksheet.
  3. Read the named style feature point 1 created with workbook.Styles.get('tableStyle') and take its Font.Size as the baseline.
  4. Raise the baseline size and write it back to the style, as the size the data range will use.
  5. Create the flag object and tick only the size, borders and centring; apply the adjusted style to the data range with sheet.Range.get('A2:E3').ApplyStyle(readBack, flag), set the column widths so the larger font size still fits, then save the workbook and dispose of it to free resources.

The full code example below shows how to load a result file, read the named style and apply it after adjusting the size in React:

function App() {
  const readAndAdjustFontStyle = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);

    // load the result file produced by feature point 1 as this run's input
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: 'ApplyStyleToRange.xlsx' });
    const sheet = workbook.Worksheets.get(0);

    // read the named style feature point 1 created and take its current font size as the baseline
    const readBack = workbook.Styles.get('tableStyle');
    const baseSize = readBack.Font.Size;

    // raise the baseline size to get the size the data range will use
    readBack.Font.Size = baseSize + 4;

    // create the flag object: this time tick only the font size, borders and centring, so the style's colour and italic stay off the data range
    const flag = new xlsModule.CellStyleFlag();
    flag.FontSize = true;
    flag.Borders = true;
    flag.HorizontalAlignment = true;
    flag.VerticalAlignment = true;

    // apply the adjusted style to the data range
    sheet.Range.get('A2:E3').ApplyStyle(readBack, flag);

    // set the column widths so the larger font size still fits
    sheet.Columns.get(0).ColumnWidth = 30;
    sheet.Columns.get(1).ColumnWidth = 20;

    // save the workbook as an Excel file
    const outputFileName = 'ReadAndAdjustFontStyle.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Read the style and adjust its size</h1>
      <button id="read-and-adjust-font-style" onClick={readAndAdjustFontStyle}>Read the named style, adjust it, then apply it</button>
    </div>
  );
}

export default App;

After running, the effect of reading the named style and adjusting its font size:

Read the named style and adjust its size


FAQ

Do the number formats already on the range change when I apply a style

Reason: No. Only the items ticked in the flag object are written into the target cells when the style is applied; the items left unticked take no part, so the number formats, borders and alignment already on the cells remain as they were.

Solution: Conversely, to apply number formats along with it, set the number format on the style as well and tick the matching item in the flag:

// give the style the number format first
tableStyle.NumberFormat = '0.00';

// tick the matching item in the flag
flag.NumberFormat = true;
sheet.Range.get('A1:E1').ApplyStyle(tableStyle, flag);

How do I apply a style to the whole worksheet

Reason: The examples above apply the style to one range at a time; a whole worksheet treated the same way would mean listing every range to cover. The worksheet object carries a method of the same name that covers the whole sheet in a single call.

Solution: Call ApplyStyle on the worksheet object to apply the whole style to the whole sheet:

sheet.ApplyStyle(tableStyle);

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.

A table has only a few seconds to be understood, and the work is done not only by the numbers but by the fonts: a title big enough to stand out, a figure that matters found at a glance, a cancelled line spotted immediately. In Excel every one of those differences is carried by a font object belonging to a single cell -- font name, size, colour, bold, italic, underline, strikethrough; changing a cell touches only that cell. One cell can also hold several fonts at once, and switching a single word to another colour or size lifts it out of the sentence around it. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; the output file is managed by a virtual file system (VFS), with no backend service required.

This article covers two feature points:

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 initialized.


Set the font formatting of a cell

Every cell carries a font object of its own, and all seven properties -- font name, size, colour, bold, italic, underline and strikethrough -- hang off that object. To see what each one does, start a worksheet of its own, write the property's name and a sample of text row by row, and set the matching property on the cell that holds the sample: a change lands on that cell alone, and the description in column A keeps its own look. The steps are:

  1. Load the font file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook() and take the first worksheet with workbook.Worksheets.get(0).
  3. Write the properties out one per row with sheet.Range.get: the name into column A and the sample text into column B, rows starting at 1 and going up by 2.
  4. Set the matching format on each row's sample through FontName, Size, Color, IsBold, IsItalic, Underline and IsStrikethrough on Range.Style.Font, taking the font colour from xlsModule.Color.get_Red() and the underline from xlsModule.FontUnderlineType.Single.
  5. Set both columns to a width of 25 with sheet.Columns.get(0).ColumnWidth and sheet.Columns.get(1).ColumnWidth.
  6. Save the workbook with workbook.SaveToFile and dispose of it with workbook.Dispose to free resources.

The full code example below shows how to set font formatting for cells in React:

function App() {
  const setCellFont = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);

    // create a new workbook and take the first worksheet
    const workbook = new xlsModule.Workbook();
    const sheet = workbook.Worksheets.get(0);

    // set the font name
    let row = 1;
    sheet.Range.get(`A${row}`).Value = 'Font name';
    sheet.Range.get(`B${row}`).Value = 'Arial';
    sheet.Range.get(`B${row}`).Style.Font.FontName = 'Arial';

    // set the font size
    row += 2;
    sheet.Range.get(`A${row}`).Value = 'Font size';
    sheet.Range.get(`B${row}`).Value = '15';
    sheet.Range.get(`B${row}`).Style.Font.Size = 15;

    // set the font colour
    row += 2;
    sheet.Range.get(`A${row}`).Value = 'Font colour';
    sheet.Range.get(`B${row}`).Value = 'Red';
    sheet.Range.get(`B${row}`).Style.Font.Color = xlsModule.Color.get_Red();

    // bold the text
    row += 2;
    sheet.Range.get(`A${row}`).Value = 'Bold';
    sheet.Range.get(`B${row}`).Value = 'Bold';
    sheet.Range.get(`B${row}`).Style.Font.IsBold = true;

    // italicise the text
    row += 2;
    sheet.Range.get(`A${row}`).Value = 'Italic';
    sheet.Range.get(`B${row}`).Value = 'Italic';
    sheet.Range.get(`B${row}`).Style.Font.IsItalic = true;

    // underline the text
    row += 2;
    sheet.Range.get(`A${row}`).Value = 'Underline';
    sheet.Range.get(`B${row}`).Value = 'Underline';
    sheet.Range.get(`B${row}`).Style.Font.Underline = xlsModule.FontUnderlineType.Single;

    // strike through the text
    row += 2;
    sheet.Range.get(`A${row}`).Value = 'Strikethrough';
    sheet.Range.get(`B${row}`).Value = 'Strikethrough';
    sheet.Range.get(`B${row}`).Style.Font.IsStrikethrough = true;

    // set the column widths
    sheet.Columns.get(0).ColumnWidth = 25;
    sheet.Columns.get(1).ColumnWidth = 25;

    // save the workbook as an Excel file
    const outputFileName = 'CellFont.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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 Cell Font Formatting</h1>
      <button id="cell-font" onClick={setCellFont}>Set the font formatting of a cell</button>
    </div>
  );
}

export default App;

After running, the effect of setting font formatting for individual cells:

Font formatting applied to cells


Mix several fonts inside one cell

A cell's rich text object lets one run of text be cut at character positions and each piece given a different font, while the sentence still reads as a whole. The way to do it is to create the font objects first and then assign them range by range with SetFont -- each call overwrites one range, so every part of the sentence has to be covered. Ranges count from 0 and include both ends. The steps are:

  1. Load the font file into the VFS with window.spire.FetchFileToVFS.
  2. Create a workbook with new Workbook() and take the first worksheet with workbook.Worksheets.get(0).
  3. Create two fonts with workbook.CreateFont(): one for the whole sentence, and one that emphasises the key phrase through IsBold and KnownColor.
  4. Take the cell that holds the sentence with sheet.Range.get('A1'), and write the whole sentence into its rich text object through RichText.Text.
  5. Work out the start and end of the text that needs the new font within the sentence with note.indexOf(key) and key.length, then assign the fonts to the three parts -- before the key phrase, the key phrase itself and after it -- with RichText.SetFont.
  6. Save the workbook with workbook.SaveToFile and dispose of it with workbook.Dispose to free resources.

The full code example below shows how to give part of the text in one cell a different font in React:

function App() {
  const setRichTextFonts = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);

    // create a new workbook and take the first worksheet
    const workbook = new xlsModule.Workbook();
    const sheet = workbook.Worksheets.get(0);

    // create the first font, for the whole sentence
    const font1 = workbook.CreateFont();
    font1.FontName = 'Arial';
    font1.Size = 12;

    // create the second font, for the key phrase
    const font2 = workbook.CreateFont();
    font2.FontName = 'Arial';
    font2.Size = 14;
    font2.IsBold = true;
    font2.KnownColor = xlsModule.ExcelColors.Red;

    // take the cell's rich text object and write the whole sentence
    const note = 'This quotation is valid for 30 days; contact your sales representative to extend it.';
    const key = '30 days';
    const richText = sheet.Range.get('A1').RichText;
    richText.Text = note;

    // locate the character range the key phrase occupies
    const start = note.indexOf(key);
    const end = start + key.length - 1;

    // set the part before the key phrase to the first font
    richText.SetFont(0, start - 1, font1);

    // set the key phrase itself to the second font
    richText.SetFont(start, end, font2);

    // keep the part after the key phrase in the first font
    richText.SetFont(end + 1, note.length - 1, font1);

    // save the workbook as an Excel file
    const outputFileName = 'RichTextFont.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, version: xlsModule.ExcelVersion.Version2016 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Mix Several Fonts Inside One Cell</h1>
      <button id="rich-text-fonts" onClick={setRichTextFonts}>Give part of the text a different font</button>
    </div>
  );
}

export default App;

After running, the effect of mixing several fonts inside one cell:

Two fonts mixed inside one cell


FAQ

What value should SetFont's end position take

Reason: Both position arguments of SetFont count from 0 and include the end position itself, so passing a string's length directly lands one character past the end.

Solution: Pass "length minus one" as the end position. To cover a whole run of text, use the sentence's length minus one:

richText.SetFont(0, note.length - 1, font1);

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.

An Excel comment usually carries a paragraph of text, explaining a number or adding a note. When the thing to be explained is itself a picture -- a supplier mark, a product photo, a sketch of a part -- text alone will not do. Spire.XLS for JavaScript can fill a comment's background with a picture, so the image appears beside the cell without a separate floating picture object being placed on the sheet. A comment's background is controlled by its fill object, and switching to a solid colour likewise changes only that one object. Spire.XLS for JavaScript performs these operations in the browser through WebAssembly; input and output files are managed by a virtual file system (VFS), with no backend service required.

This article covers two feature points:

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 initialized.


Insert a picture comment into a cell

A comment is attached to a cell; giving one a picture means first obtaining the comment object on the target cell and then filling it. The picture becomes the comment box's background, replacing the default pale-yellow shading, and it is this picture that appears when the mouse moves over the cell or the file is opened. The picture is scaled to the size of the comment box, and the default box is a flat rectangle, so a square picture is stretched out of shape; after filling, the width and height must therefore be set so that the box matches the picture's proportions. Where the box sits on the sheet is decided by a separate pair of position properties, so it does not have to follow the cell border. The steps are:

  1. Load the font, the picture and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Take the cell the comment will be attached to with sheet.Range.get('A4').
  4. Create a comment object on the cell with range.AddComment().
  5. Fill the comment background with the picture using comment.Fill.CustomPicture, passing the picture as a stream built by new xlsModule.Stream('logo.png') and 'None' for the name argument.
  6. Set comment.Width and comment.Height so the comment box is square, matching the square picture, which keeps the picture from being stretched.
  7. Place the comment box relative to the top-left corner of the worksheet with comment.Top and comment.Left, so that it covers the content below without obscuring its own row.
  8. Set comment.Visible to show the comment as soon as the file is opened.
  9. Save the workbook with workbook.SaveToFile.
  10. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to insert a picture comment into an Excel cell in React:

function App() {
  const addPictureComment = 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, the picture and the test data file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    await window.spire.FetchFileToVFS('logo.png', '', `${process.env.PUBLIC_URL}data/`);
    const inputFileName = 'ProductList.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // take the first worksheet and add a comment to cell A4, filled with the picture
    const sheet = workbook.Worksheets.get(0);
    const range = sheet.Range.get('A4');
    const comment = range.AddComment();
    comment.Fill.CustomPicture({ im: new xlsModule.Stream('logo.png'), name: 'None' });

    // set the width and height of the comment box
    comment.Width = 150;
    comment.Height = 150;

    // place the comment box relative to the top-left corner of the worksheet
    comment.Top = 100;
    comment.Left = 400;

    // make the comment show as soon as the file is opened
    comment.Visible = true;

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

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Insert Comments with Pictures</h1>
      <button id="picture-comment" onClick={addPictureComment}>Insert a picture comment into a cell</button>
    </div>
  );
}

export default App;

After running, the effect of inserting a picture comment into a cell:

Picture comment inserted into a cell


Fill a comment with a solid colour

A comment background need not be a picture. When all that is needed is for one comment to stand out from the pale-yellow shading around it, specifying a solid colour is more straightforward than preparing an image. Filling with a picture and filling with a solid colour use the same comment.Fill object; only the fill type changes from picture to solid. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Take the cell the comment will be attached to with sheet.Range.get('A4') and create the comment with range.AddComment().
  4. Write the comment text with comment.Text.
  5. Create a font with workbook.CreateFont() and set the whole text to it with comment.RichText.SetFont.
  6. Set comment.Fill.FillType to the solid fill type, xlsModule.ShapeFillType.SolidColor.
  7. Pick the colour with comment.Fill.ForeColor.
  8. Set comment.Visible to show the comment as soon as the file is opened.
  9. Save the workbook with workbook.SaveToFile.
  10. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to fill an Excel comment with a solid colour in React:

function App() {
  const fillCommentColor = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ProductList.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // take the first worksheet and add a comment to cell A4
    const sheet = workbook.Worksheets.get(0);
    const range = sheet.Range.get('A4');

    // create the font the comment text will use
    const font = workbook.CreateFont();
    font.FontName = 'Arial';
    font.Size = 11;
    font.KnownColor = xlsModule.ExcelColors.Orange;

    // write the comment text and set the whole text to that font
    const commentText = 'This product leads the season';
    range.Comment.Text = commentText;
    range.Comment.RichText.SetFont(0, commentText.length - 1, font);

    // set the comment fill to a solid colour and pick the colour
    range.Comment.Fill.FillType = xlsModule.ShapeFillType.SolidColor;
    range.Comment.Fill.ForeColor = xlsModule.Color.get_SkyBlue();
    range.Comment.Visible = true;

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

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Insert Comments with Pictures</h1>
      <button id="comment-fill-color" onClick={fillCommentColor}>Fill a comment with a solid colour</button>
    </div>
  );
}

export default App;

After running, the effect of filling a comment with a solid colour:

Comment filled with a solid colour


FAQ

Why does the picture comment not appear when the result file is opened

Reason: Comments are hidden by default and appear only when the mouse moves over the cell. If the comment was not set to visible before saving, reopening the file shows an ordinary worksheet, and the picture comment does not appear on its own.

Solution: Set comment.Visible to true, and the comment appears as soon as the file is opened:

comment.Visible = true;

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.

A shape in Excel is usually there to point at something: the arrow on a diagram, the triangle that flags a number, the cloud that circles an area. Give it a hyperlink and the drawing becomes an entry point a reader can click. A shape, like a picture, is not a cell, and its hyperlink lives on the shape object itself, so both setting and reading one go through the worksheet's shape collection rather than its cell hyperlink collection. Spire.XLS for JavaScript does this in the browser on WebAssembly, handling input and output files through a virtual file system (VFS) with no backend service.

This article covers two core feature points:

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.


Set a hyperlink on a shape

Setting a hyperlink on a shape is what turns a drawing into a clickable entry point: a reader who clicks the shape on the worksheet opens the address it points at. The link belongs to the shape itself rather than to a cell, so the shape has to be taken from the worksheet's shape collection first, and the address handed to its own link object. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Add the shapes into the space left for them with sheet.PrstGeomShapes.AddPrstGeomShape.
  4. Take the shape count from sheet.PrstGeomShapes.Count and walk every shape on the worksheet with it.
  5. Take each shape's link object from its HyLink property and write the address into its Address.
  6. Save the workbook with workbook.SaveToFile.
  7. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to set a hyperlink on a shape in Excel in React:

function App() {
  const shapeHyperlink = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ShapeLink.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // take the first worksheet and add the three shapes into the space left for them
    const sheet = workbook.Worksheets.get(0);
    const shapes = sheet.PrstGeomShapes;
    shapes.AddPrstGeomShape(10, 1, 90, 60, xlsModule.PrstGeomShapeType.Triangle);
    shapes.AddPrstGeomShape(10, 3, 90, 60, xlsModule.PrstGeomShapeType.CurvedRightArrow);
    shapes.AddPrstGeomShape(10, 5, 90, 60, xlsModule.PrstGeomShapeType.Cloud);

    // set a hyperlink on each shape in turn
    const addresses = [
      'https://www.e-iceblue.com/Introduce/xls-for-javascript.html',
      'https://www.e-iceblue.com/Buy/Spire.XLS-JavaScript.html',
      'https://www.e-iceblue.com/Download/Spire-Office-JavaScript.html',
    ];
    for (let i = 0; i < shapes.Count; i++) {
      shapes.get(i).HyLink.Address = addresses[i];
    }

    // save the workbook
    const outputFileName = 'ShapeHyperlink.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Version2010 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Add Hyperlinks to Shapes in Excel</h1>
      <button id="shape-hyperlink" onClick={shapeHyperlink}>Set a hyperlink on the shape</button>
    </div>
  );
}

export default App;

After running, the effect of setting a hyperlink on a shape:

Set a hyperlink on a shape in Excel


Read the hyperlink from a shape

Reading the hyperlink off a shape is how a finished sheet is checked over: whether every shape carries a link, and whether they point at the right addresses. The addresses that come back are written out as a text file, which makes them easy to line up against a list of links. Shape hyperlinks are not kept in the cell hyperlink collection, so the shapes have to be taken from the worksheet one by one, and each one asked for its own link object. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Take the shape count from sheet.PrstGeomShapes.Count and walk every shape on the worksheet with it.
  4. Take this shape's link object from its HyLink property.
  5. Read the target address from the link object's Address, collecting the addresses as you go.
  6. Write the collected addresses into a text file with FS.writeFile, one address per line.
  7. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to read the hyperlink from a shape in Excel in React:

function App() {
  const readShapeHyperlink = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'ShapeHyperlinks.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // walk the shapes of the worksheet and read the hyperlink off each one
    const sheet = workbook.Worksheets.get(0);
    const shapes = sheet.PrstGeomShapes;
    const addresses = [];
    for (let i = 0; i < shapes.Count; i++) {
      addresses.push(shapes.get(i).HyLink.Address);
    }

    // write the addresses that were read into a text file
    const outputFileName = 'ReadShapeHyperlink.txt';
    window.dotnetRuntime.Module.FS.writeFile(outputFileName, addresses.join('\n'));

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'text/plain' });
    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 Hyperlinks to Shapes in Excel</h1>
      <button id="read-shape-hyperlink" onClick={readShapeHyperlink}>Read the hyperlink from the shape</button>
    </div>
  );
}

export default App;

After running, the effect of reading the hyperlink from a shape:

Read the hyperlink from a shape in Excel


FAQ

Why does a shape's hyperlink not show up in sheet.HyperLinks?

Cause: sheet.HyperLinks collects cell hyperlinks only. A shape is not a cell; it belongs to the worksheet's shape collection, and the link hangs on the shape object's HyLink.

Solution: Go through the shape collection. In this example, after a hyperlink was set on the shapes, sheet.HyperLinks.Count was still 0, and it was still 0 after the file was saved and opened again. Read a shape's link with sheet.PrstGeomShapes.get(i).HyLink.Address.

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.

Excel workbooks often need to tie scattered content together: one click on a summary page jumps to a detail worksheet, one click on a quote opens the source file kept beside it. That "jump elsewhere" ability belongs to cell hyperlinks, and the link type decides where the jump goes -- another worksheet inside the same workbook, or a file outside it. Spire.XLS for JavaScript does this in the browser through WebAssembly; input and output files are managed by a virtual file system (VFS), with no backend service required.

This article covers two feature points:

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 initialized.


Add a link to another worksheet

A cross-sheet link solves the "jump from here to there" problem inside one workbook: it strings the content scattered across worksheets into a clickable path, so that the reader does not have to hunt for the right sheet. A link sits on a cell, so the first move is to create a hyperlink object on the target cell and then say, in turn, where it jumps and what text it shows. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Create a hyperlink object on the target cell with sheet.HyperLinks.Add.
  4. Mark the type as a workbook-internal link with the hyperlink's Type property, set to xlsModule.HyperLinkType.Workbook.
  5. Set the text shown on the cell with the TextToDisplay property.
  6. Write the jump target into the Address property, in the form "sheet name!cell", for example Data!B7.
  7. Style the link text blue and underlined so that it reads differently from ordinary text.
  8. Save the workbook with workbook.SaveToFile.
  9. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to add a cross-sheet hyperlink to an Excel cell in React:

function App() {
  const linkToOtherSheet = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SheetLink.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // take the first worksheet and add a hyperlink object to cell B5
    const sheet = workbook.Worksheets.get(0);
    const cell = sheet.Range.get('B5');
    const hyperlink = sheet.HyperLinks.Add({ range: cell });

    // mark it as a workbook-internal link that points at cell B7 of the second worksheet
    hyperlink.Type = xlsModule.HyperLinkType.Workbook;
    hyperlink.TextToDisplay = 'View the full-year total in the data sheet';
    hyperlink.Address = 'Data!B7';

    // colour the link text blue and underline it so that it reads as a link
    cell.Style.Font.Color = xlsModule.Color.get_Blue();
    cell.Style.Font.Underline = xlsModule.FontUnderlineType.Single;

    // save the workbook
    const outputFileName = 'LinkToOtherSheetCell.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Version2010 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Add Cross-Sheet and External File Links in Excel</h1>
      <button id="link-to-other-sheet" onClick={linkToOtherSheet}>Add a link to another worksheet</button>
      <button id="link-to-external-file" onClick={linkToExternalFile}>Add a link to an external file</button>
    </div>
  );
}

export default App;

After running, the effect of adding a cross-sheet link:

Add a link to another worksheet to an Excel cell


Add a link to an external file

A cross-sheet link can only jump within the same workbook, so once the target falls outside it, the link has to become a file link. It differs from a cross-sheet link in just two places: the type is marked as a file link, and the jump target becomes a file path -- either a full path or a path relative to the current workbook. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Create a hyperlink object on the target cell with sheet.HyperLinks.Add.
  4. Mark the type as a file link with the Type property, set to xlsModule.HyperLinkType.File.
  5. Set the text shown on the cell with the TextToDisplay property.
  6. Write the path of the external file into the Address property, for example ../files/Quote.xlsx.
  7. Style the link text blue and underlined so that it reads differently from ordinary text.
  8. Save the workbook with workbook.SaveToFile.
  9. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to add an external-file hyperlink to an Excel cell in React:

function App() {
  const linkToExternalFile = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'SheetLink.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // take the first worksheet and add a hyperlink object to cell B6
    const sheet = workbook.Worksheets.get(0);
    const cell = sheet.Range.get('B6');
    const hyperlink = sheet.HyperLinks.Add({ range: cell });

    // mark it as a file link that points at a file outside the workbook
    hyperlink.Type = xlsModule.HyperLinkType.File;
    hyperlink.TextToDisplay = 'Open the external quote';
    hyperlink.Address = '../files/Quote.xlsx';

    // colour the link text blue and underline it so that it reads as a link
    cell.Style.Font.Color = xlsModule.Color.get_Blue();
    cell.Style.Font.Underline = xlsModule.FontUnderlineType.Single;

    // save the workbook
    const outputFileName = 'LinkToExternalFile.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Version2010 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Add Cross-Sheet and External File Links in Excel</h1>
      <button id="link-to-other-sheet" onClick={linkToOtherSheet}>Add a link to another worksheet</button>
      <button id="link-to-external-file" onClick={linkToExternalFile}>Add a link to an external file</button>
    </div>
  );
}

export default App;

After running, the effect of adding an external-file link:

Add a link to an external file to an Excel cell


FAQ

Why does adding a link to a cell that already has text replace that text with the address

Reason: The text a cell shows is decided by TextToDisplay. Create a link without setting it and the address moves up to serve as the display text; the replacement happens the moment the link is added, so reading Range.Text afterwards returns the address rather than the original text.

Solution: Read the text before adding the link, then hand it back to TextToDisplay once the link is in place:

const cell = sheet.Range.get('B6');
const text = cell.Text;                  // keep the original text before adding the link
const hyperlink = sheet.HyperLinks.Add({ range: cell });
hyperlink.Type = xlsModule.HyperLinkType.File;
hyperlink.Address = '../files/Quote.xlsx';
hyperlink.TextToDisplay = text;          // hand the text back, so the cell still shows it

How do I remove a link but keep the text in the cell

Reason: The link and the text are two different things -- the link hangs on the cell, while the text is the cell's content. Range.ClearAll() clears the content, so using it to get rid of a link takes the text with it.

Solution: Remove the link by index with sheet.HyperLinks.RemoveAt, and the cell text stays as it was. For example, sheet.HyperLinks.RemoveAt(0) removes the first link; the text is still in the cell afterwards, it simply is no longer clickable.

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.

A picture in Excel is more than decoration. Put a logo or a product shot on a worksheet and give it a hyperlink, and the file stops being a plain description and becomes something a reader can click through. A picture is not a cell, though, and its hyperlink lives on the picture object, so both setting and reading one go through the worksheet's picture collection rather than its cell hyperlink collection. Spire.XLS for JavaScript does this in the browser on WebAssembly, handling input and output files through a virtual file system (VFS) with no backend service.

This article covers two core feature points:

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.


Set a hyperlink on a picture

Setting a hyperlink on a picture is what turns it into a clickable entry point: a reader who clicks the picture on the worksheet opens the address it points at. The link belongs to the picture itself rather than to a cell, so the picture object has to be taken from the worksheet's picture collection first, and the address handed to that object. The steps are:

  1. Load the font, the picture and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Insert the picture into the space left for it with sheet.Pictures.Add.
  4. Set the hyperlink on the picture with picture.SetHyperLink, passing true as the second argument.
  5. Save the workbook with workbook.SaveToFile.
  6. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to set a hyperlink on a picture in Excel in React:

function App() {
  const pictureHyperlink = 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, the picture and the test data file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    await window.spire.FetchFileToVFS('logo.png', '', `${process.env.PUBLIC_URL}data/`);
    const inputFileName = 'PictureLink.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // take the first worksheet and drop the picture into the space left for it
    const sheet = workbook.Worksheets.get(0);
    const picture = sheet.Pictures.Add({ topRow: 10, leftColumn: 1, fileName: 'logo.png' });

    // set the hyperlink on the picture; the second argument being true means it points outside the workbook
    picture.SetHyperLink('https://www.e-iceblue.com/Misc/about-us.html', true);

    // save the workbook
    const outputFileName = 'PictureHyperlink.xlsx';
    workbook.SaveToFile({ fileName: outputFileName, fileFormat: xlsModule.FileFormat.Version2010 });

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start 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>Add Hyperlinks to Pictures in Excel</h1>
      <button id="picture-hyperlink" onClick={pictureHyperlink}>Set a hyperlink on the picture</button>
    </div>
  );
}

export default App;

After running, the effect of setting a hyperlink on a picture:

Set a hyperlink on a picture in Excel


Read the hyperlink from a picture

Reading the hyperlink off a picture is how a finished workbook is checked over: whether the links are all there, and whether they point at the right addresses. The addresses that come back are written out as a text file, which makes them easy to line up against a list of links. Picture hyperlinks are not kept in the cell hyperlink collection, so the pictures have to be taken from the worksheet one by one, and each one asked for its own link object. The steps are:

  1. Load the font and the test data file into the VFS.
  2. Load the workbook with workbook.LoadFromFile.
  3. Take the picture count from sheet.Pictures.Count and walk every picture on the worksheet with it.
  4. Take this picture's hyperlink object with picture.GetHyperLink.
  5. Read the target address from the link object's Address, collecting the addresses as you go.
  6. Write the collected addresses into a text file with FS.writeFile, one address per line.
  7. Dispose of the workbook with workbook.Dispose to free resources.

The complete code example below shows how to read the hyperlink from a picture in Excel in React:

function App() {
  const readPictureHyperlink = 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 file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'PictureHyperlinks.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

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

    // walk the pictures of the worksheet and read the hyperlink off each one
    const sheet = workbook.Worksheets.get(0);
    const pictures = sheet.Pictures;
    const addresses = [];
    for (let i = 0; i < pictures.Count; i++) {
      const link = pictures.get(i).GetHyperLink();
      addresses.push(link.Address);
    }

    // write the addresses that were read into a text file
    const outputFileName = 'ReadPictureHyperlink.txt';
    window.dotnetRuntime.Module.FS.writeFile(outputFileName, addresses.join('\n'));

    // dispose of the workbook to free resources
    workbook.Dispose();

    // read the result file from the VFS and start the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'text/plain' });
    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 Hyperlinks to Pictures in Excel</h1>
      <button id="read-picture-hyperlink" onClick={readPictureHyperlink}>Read the hyperlink from the picture</button>
    </div>
  );
}

export default App;

After running, the effect of reading the hyperlink from a picture:

Read the hyperlink from a picture in Excel


FAQ

Why does a picture's hyperlink not show up in sheet.HyperLinks?

Cause: sheet.HyperLinks collects cell hyperlinks only. A picture is not a cell; it belongs to the worksheet's picture collection, and the link hangs on the picture object itself.

Solution: Go through the picture collection. In this example, after a hyperlink was set on the picture, sheet.HyperLinks.Count was still 0, and it was still 0 after the file was saved and opened again. Read a picture's link with sheet.Pictures.get(i).GetHyperLink(), then read the Address off the link object.

When should the second argument of SetHyperLink be false?

Cause: The second argument of picture.SetHyperLink(address, isExternal) says whether the target lies outside the workbook.

Solution: Pass true for a web page or an external file, and the link type comes out as HyperLinkType.Url; pass false for a position inside the same workbook, write the address as a cell reference such as Sheet1!A1, and the type comes out as HyperLinkType.Workbook. In this example both forms were saved, and after the file was opened again the Address and Type read back matched what had been set.

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.

Page 1 of 7
page 1