docs.sheetjs.com/docz/docs/03-demos/01-frontend/08-bundler.md

40 KiB

title pagination_prev pagination_next sidebar_position sidebar_custom_props
Bundlers demos/index demos/grid/index 8
skip
1

import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock';

SheetJS predates ECMAScript modules and bundler tools like Webpack. As best practices have evolved, stress testing SheetJS libraries have revealed bugs in the respective bundlers. This demo collects various notes and provides basic examples.

:::note

Issues should be reported to the respective bundler projects. Typically it is considered a bundler bug if the tool cannot properly handle JS libraries.

:::

Browserify

browserify is compatible with the library and should "just work" with the require form in a main page or in a web worker:

var XLSX = require("xlsx");
// ... use XLSX ...

After installing the NodeJS module, bundling is straightforward:

browserify app.js > browserify.js
uglifyjs browserify.js > browserify.min.js

Web Worker scripts can be bundled using the same approach.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against Browserify 17.0.0

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Download the following files:
curl -LO https://docs.sheetjs.com/browserify/app.js
curl -LO https://docs.sheetjs.com/browserify/index.html
curl -LO https://docs.sheetjs.com/browserify/xlsxworker.js
  1. Bundle the scripts:
npx browserify app.js > browserify.js
npx browserify xlsxworker.js > worker.js
  1. Spin up a local web server:
npx http-server
  1. Access the site http://localhost:8080/ and use the file input element to select a spreadsheet.

Bun

bun bun is capable of optimizing imported libraries in node_modules. In local testing, a bundled script can save tens of milliseconds per run.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against Bun 0.5.9

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Save the following script to bun.js:
// highlight-next-line
import * as XLSX from 'xlsx';
// highlight-next-line
import * as fs from 'fs';
// highlight-next-line
XLSX.set_fs(fs);

/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter((row) => row.terms.some((term) => term.type === "prez"));

/* flatten objects */
const rows = prez.map((row) => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = XLSX.utils.json_to_sheet(rows);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
XLSX.writeFile(workbook, "Presidents.xlsx");
  1. Bundle the script with bun bun:
bun bun bun.js

This procedure will generate node_modules.bun.

  1. Run the script
bun bun.js

Dojo

Integration details are included in the "AMD" installation

Complete Examples are included in the "Dojo" demo

esbuild

The xlsx.mjs source file uses a subset of ES6 that esbuild understands and is able to transpile for older browsers.

Both the node and browser platforms work out of the box.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against esbuild 0.17.18

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Save the following to esbrowser.js:
// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx';

(async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
})();
  1. Create a small HTML page that loads the script. Save to index.html:
<body><script src="esb.browser.js"></script></body>
  1. Create bundle:
npx esbuild@0.17.18 esbrowser.js --bundle --outfile=esb.browser.js
  1. Start a local HTTP server, then go to http://localhost:8080/
npx http-server .
  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Save the following to esbnode.js:
// highlight-next-line
import { set_fs, utils, version, writeFile } from 'xlsx';
// highlight-next-line
import * as fs from 'fs';
// highlight-next-line
set_fs(fs);

(async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFile(workbook, "Presidents.xlsx");
})();
  1. Create bundle:
npx esbuild@0.17.18 esbnode.js --bundle --platform=node --outfile=esb.node.js
  1. Run the bundle:
node esb.node.js

:::note

Bundling raw data is supported using the binary loader. For more advanced content workflows, ViteJS is the recommended tool.

:::

Parcel

Parcel should play nice with SheetJS out of the box.

:::warning Parcel Bug

Errors of the form Could not statically evaluate fs call stem from a Parcel bug. Upgrade to Parcel version 1.5.0 or later.

:::

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against parcel 2.8.3

:::

This demo follows the Presidents Example.

  1. Save the following to index.html:
<body>
<h3>SheetJS <span id="vers"></span> export demo</h3>
<button id="xport">Click to Export!</button>
<!-- the script tag must be marked as `type="module"` -->
<!-- highlight-next-line -->
<script type="module">
// ESM-style import from "xlsx"
// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx';

document.getElementById("vers").innerText = version;
document.getElementById("xport").onclick = async() => {
  /* fetch JSON data and parse */
  const url = "https://sheetjs.com/data/executive.json";
  const raw_data = await (await fetch(url)).json();

  /* filter for the Presidents */
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

  /* flatten objects */
  const rows = prez.map(row => ({
    name: row.name.first + " " + row.name.last,
    birthday: row.bio.birthday
  }));

  /* generate worksheet and workbook */
  const worksheet = utils.json_to_sheet(rows);
  const workbook = utils.book_new();
  utils.book_append_sheet(workbook, worksheet, "Dates");

  /* fix headers */
  utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

  /* calculate column width */
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
  worksheet["!cols"] = [ { wch: max_width } ];

  /* create an XLSX file and try to save to Presidents.xlsx */
  writeFileXLSX(workbook, "Presidents.xlsx");
};
</script>
<body>
  1. Install the SheetJS node module:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Run the Parcel CLI tool:
npx -y parcel@2.8.3 index.html
  1. Access the page listed in the output (typically http://localhost:1234) and click the "Click to Export!" button to generate a file.

RequireJS

Standalone scripts comply with AMD define semantics, enabling use in RequireJS out of the box.

To enable use of the alias xlsx, the RequireJS config should set an alias in the paths property:

require.config({
  baseUrl: ".",
  name: "app",
  paths: {
    // highlight-next-line
    xlsx: "xlsx.full.min"
  }
});
// highlight-next-line
require(["xlsx"], function(XLSX) {
  /* use XLSX here */
  console.log(XLSX.version);
});

The Live demo loads RequireJS from the CDN, uses it to load the standalone script from the SheetJS CDN, and uses the XLSX variable to create a button click handler that creates a workbook.

The r.js optimizer also supports the standalone scripts.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against RequireJS 2.3.3

:::

:::caution

The r.js optimizer does not support ES6 syntax including arrow functions and the async keyword! The demo JS code uses traditional functions.

:::

  1. Download the standalone build:

{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js}

  1. Save the following to index.html:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script src="http://requirejs.org/docs/release/2.3.3/comments/require.js"></script>
    <script>
/* Wire up RequireJS */
require.config({
  baseUrl: ".",
  name: "SheetJSRequire",
  paths: {
    xlsx: "./xlsx.full.min"
  }
});
    </script>
    <script src="SheetJSRequire.js"></script>
  </body>
</html>
  1. Save the following to SheetJSRequire.js:
require(["xlsx"], function(XLSX) {
  document.getElementById("xport").addEventListener("click", function() {
    /* fetch JSON data and parse */
    var url = "https://sheetjs.com/data/executive.json";
    fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {

    /* filter for the Presidents */
    var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });

    /* flatten objects */
    var rows = prez.map(function(row) { return {
      name: row.name.first + " " + row.name.last,
      birthday: row.bio.birthday
    }; });

    /* generate worksheet and workbook */
    var worksheet = XLSX.utils.json_to_sheet(rows);
    var workbook = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");

    /* fix headers */
    XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

    /* calculate column width */
    var max_width = rows.reduce(function(w, r) { return Math.max(w, r.name.length); }, 10);
    worksheet["!cols"] = [ { wch: max_width } ];

    /* create an XLSX file and try to save to Presidents.xlsx */
    XLSX.writeFileXLSX(workbook, "Presidents.xlsx");
    });
  });
});
  1. Start a local HTTP server, then go to http://localhost:8080/
npx http-server .

Click on "Click here to export" to generate a file.

  1. Create build.js configuration for the optimizer:
({
  baseUrl: ".",
  name: "SheetJSRequire",
  paths: {
    xlsx: "./xlsx.full.min"
  },
  out: "SheetJSRequire.min.js"
});
  1. Run the r.js optimizer to create SheetJSRequire.min.js:
npx -p requirejs@2.3.3 r.js -o build.js
  1. Save the following to optimized.html:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script src="http://requirejs.org/docs/release/2.3.3/comments/require.js"></script>
    <script src="SheetJSRequire.min.js"></script>
  </body>
</html>
  1. Open http://localhost:8080/optimized.html

Click on "Click here to export" to generate a file.

Rollup

Rollup requires @rollup/plugin-node-resolve to support NodeJS modules:

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against Rollup 3.21.5

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@3.21.5 @rollup/plugin-node-resolve {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@3.21.5 @rollup/plugin-node-resolve {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} rollup@3.21.5 @rollup/plugin-node-resolve
  1. Save the following to index.js:
// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx';

document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
  1. Bundle the script:
npx rollup index.js --plugin @rollup/plugin-node-resolve --file bundle.js --format iife
  1. Create a small HTML page that loads the script. Save to index.html:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script type="module" src="./bundle.js"></script>
  </body>
</html>
  1. Start a local HTTP server, then go to http://localhost:8080/
npx http-server .

Click on "Click here to export" to generate a file.

Snowpack

Snowpack works with no caveats.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against Snowpack 3.8.8

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Save the following to index.js:
// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx';

document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
  1. Create a small HTML page that loads the script. Save to index.html:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script type="module" src="./index.js"></script>
  </body>
</html>

:::note

Unlike other bundlers, Snowpack requires a full page including HEAD element.

:::

  1. Build for production:
npx snowpack@3.8.8 build
  1. Start a local HTTP server, then go to http://localhost:8080/
npx http-server build/

Click on "Click here to export" to generate a file.

SWC

SWC provides spack for bundling scripts.

:::warning

When this demo was last tested, there was a bug affecting 1.2.247 and 1.3 . It is strongly recommended to use @swc/core@1.2.245 until the bug is fixed.

:::

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against SWC 1.2.246

:::

  1. Install the dependencies using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246 {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} regenerator-runtime @swc/cli @swc/core@1.2.246

:::note

The regenerator-runtime dependency is used for transpiling fetch and is not required if the interface code does not use fetch or Promises.

:::

  1. Save the following to index.js:
import { utils, version, writeFileXLSX } from 'xlsx';

document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
  1. Create an spack.config.js config file:
const { config } = require('@swc/core/spack');

module.exports = ({
  entry: {
    'web': __dirname + '/index.js',
  },
  output: {
    path: __dirname + '/lib'
  },
  module: {},
});
  1. Build for production:
npx spack

This command will create the script lib/web.js

  1. Create a small HTML page that loads the generated script:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script src="lib/web.js"></script>
  </body>
</html>
  1. Start a local HTTP server, then go to http://localhost:8080/
npx http-server

Click on "Click here to export" to generate a file.

SystemJS

With configuration, SystemJS supports both browser and NodeJS deployments.

:::caution

This demo was written against SystemJS 0.19, the most popular SystemJS version used with Angular applications. In the years since the release, Angular and other tools using SystemJS have switched to Webpack.

:::

SystemJS fails by default because the library does not export anything in the web browser. The meta configuration option can be used to expose XLSX:

{\ SystemJS.config({ meta: { 'xlsx': { exports: 'XLSX' // <-- tell SystemJS to expose the XLSX variable } }, map: { 'xlsx': 'https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js', 'fs': '', // <--| 'crypto': '', // <--| suppress native node modules 'stream': '' // <--| } }); SystemJS.import('main.js'); // load \main.js``}

The main.js script can freely require("xlsx").

:::caution Web Workers

Web Workers can load the SystemJS library with importScripts, but the imported code cannot assign the original worker's onmessage callback. The recommended approach is to expose a global from the required script, For example, supposing the shared name is _cb, the primary worker script would call the callback:

/* main worker script */
importScripts('system.js');

SystemJS.config({ /* ... browser config ... */ });

onmessage = function(evt) {
  SystemJS.import('workermain.js').then(function() { _cb(evt); });
};

The worker script would define and expose the function:

/* Loaded with SystemJS import */
var XLSX = require('xlsx');

_cb = function(evt) { /* ... do work here ... */ };

:::

:::caution

While SystemJS works in NodeJS, the built-in require should be preferred.

:::

The NodeJS module main script is xlsx/xlsx.js and should be mapped:

SystemJS.config({
  map: {
    "xlsx": "./node_modules/xlsx/xlsx.js"
  }
});

The standalone scripts require a hint that the script assigns a global:

SystemJS.config({
  meta: {
    "standalone": { format: "global" }
  },
  map: {
    "standalone": "xlsx.full.min.js"
  }
});
Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against SystemJS 0.20.16

:::

The Live demo loads SystemJS from the CDN, uses it to load the standalone script from the SheetJS CDN and emulate a require implementation when loading main.js

"View Source" works on the main HTML page and the main.js script.

:::note

This demo was last tested on 2023 May 07 against SystemJS 0.19.47

:::

  1. Install the dependencies:

{\ npm init -y npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz systemjs@0.19.47}

  1. Save the following script to SheetJSystem.js:
const SystemJS = require('systemjs');

// highlight-start
SystemJS.config({
  map: {
    'xlsx': 'node_modules/xlsx/xlsx.js',
    'fs': '@node/fs',
    'crypto': '@node/crypto',
    'stream': '@node/stream'
  }
});
// highlight-end

SystemJS.import('xlsx').then(async function(XLSX) {

  /* fetch JSON data and parse */
  const url = "https://sheetjs.com/data/executive.json";
  const raw_data = await (await fetch(url)).json();

  /* filter for the Presidents */
  const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

  /* flatten objects */
  const rows = prez.map(row => ({
    name: row.name.first + " " + row.name.last,
    birthday: row.bio.birthday
  }));

  /* generate worksheet and workbook */
  const worksheet = XLSX.utils.json_to_sheet(rows);
  const workbook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(workbook, worksheet, "Dates");

  /* fix headers */
  XLSX.utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

  /* calculate column width */
  const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
  worksheet["!cols"] = [ { wch: max_width } ];

  /* create an XLSX file and try to save to Presidents.xlsx */
  XLSX.writeFile(workbook, "Presidents.xlsx");

});
  1. Run in NodeJS:
node SheetJSystem.js

If the demo worked, Presidents.xlsx will be created.

:::note

As it uses fetch, this demo requires Node 18.

:::

Vite

ViteJS is compatible with SheetJS versions starting from 0.18.10.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against ViteJS 4.3.5

:::

  1. Create a new ViteJS project:
npm create vite@latest sheetjs-vite -- --template vue-ts
cd sheetjs-vite
npm i
  1. Add the SheetJS dependency:

{\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz}

  1. Replace src\components\HelloWorld.vue with:
<script setup lang="ts">
import { version, utils, writeFileXLSX } from 'xlsx';

interface President {
  terms: { "type": "prez" | "viceprez"; }[];
  name: { first: string; last: string; }
  bio: { birthday: string; }
}

async function xport() {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data: President[] = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
}

</script>

<template>
  <button type="button" @click="xport">Export with SheetJS version {{ version }}</button>
</template>
  1. Run npm run dev and test functionality by opening a web browser to http://localhost:5173/ and clicking the button

  2. Run npx vite build and verify the generated pages work by running a local web server in the dist folder:

npx http-server dist/

Access http://localhost:8080 in your web browser and click the export button.

:::note

The Vite section of the Content demo covers asset loaders. They are ideal for static sites pulling data from sheets at build time.

:::

Webpack

The ECMAScript Module build has no require or import statements and does not use process or any variable that Webpack could interpret as a NodeJS feature. Various package.json fields have been added to appease various Webpack versions starting from the 2.x series.

:::note CommonJS and ESM

Webpack bundled the CommonJS build in older versions of the library. Version 0.18.1 changed the NodeJS module package so that Webpack uses the ESM build.

The ESM build does not include the codepage support library for XLS reading. As described in the installation instructions, the codepage dependency should be imported explicitly:

import * as XLSX from 'xlsx';
import * as cptable from 'xlsx/dist/cpexcel.full.mjs';
set_cptable(cptable);

:::

:::caution

Some older webpack projects will throw an error in the browser:

require is not defined             (xlsx.mjs)

This was a bug in Webpack and affected projects built with create-react-app. If upgrading Webpack is not feasible, explicitly import the standalone builds:

import * as XLSX from 'xlsx/dist/xlsx.full.min.js';

:::

Complete Example (click to show)

:::note

This demo was tested against the following Webpack versions:

Version Date Required Workarounds
2.7.0 2023-05-07 Import xlsx/dist/xlsx.full.min.js
3.12.0 2023-05-07 Import xlsx/dist/xlsx.full.min.js
4.46.0 2023-05-07 Downgrade NodeJS (tested v16.20.0)
5.82.0 2023-05-07

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Save the following to index.js:
// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx';

document.getElementById("xport").addEventListener("click", function() {
  /* fetch JSON data and parse */
  var url = "https://sheetjs.com/data/executive.json";
  fetch(url).then(function(res) { return res.json(); }).then(function(raw_data) {

    /* filter for the Presidents */
    var prez = raw_data.filter(function(row) { return row.terms.some(function(term) { return term.type === "prez"; }); });

    /* flatten objects */
    var rows = prez.map(function(row) { return {
      name: row.name.first + " " + row.name.last,
      birthday: row.bio.birthday
    }; });

    /* generate worksheet and workbook */
    var worksheet = utils.json_to_sheet(rows);
    var workbook = utils.book_new();
    utils.book_append_sheet(workbook, worksheet, "Dates");

    /* fix headers */
    utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

    /* calculate column width */
    var max_width = rows.reduce(function(w, r) { return Math.max(w, r.name.length); }, 10);
    worksheet["!cols"] = [ { wch: max_width } ];

    /* create an XLSX file and try to save to Presidents.xlsx */
    writeFileXLSX(workbook, "Presidents.xlsx");
  });
});
  1. Create a small webpack.config.js script that writes to index.min.js:
module.exports = {
  /* entry point index.js */
  entry: './index.js',

  /* write to index.min.js */
  output: { path:__dirname, filename: './index.min.js' }
}
  1. Build for production. The command depends on the version of webpack:

:::note

In Webpack 2.x and 3.x, the import statement must use the standalone build:

// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx/dist/xlsx.full.min.js';

:::

This line must be changed before bundling.

Webpack 2.x

npx webpack@2.x -p

Webpack 3.x

npx webpack@3.x -p

:::caution

The minifier that ships with Webpack 2.x does not handle async functions. The unminified code generated by Webpack will work for the purposes of the demo. It is strongly recommended to upgrade to a newer version of Webpack. If that is not feasible, the example should be replaced with a traditional Promise chain.

:::

:::warning Pinning specific versions of webpack

The webpack tooling is not designed for switching between versions. A specific version above 4.0 can be pinned by locally installing webpack and the CLI tool.

:::

Webpack 4.x

:::note

Webpack 4 is incompatible with Node 18+. When this demo was last tested, NodeJS was locally downgraded to 16.20.0

:::

npm i --save webpack@4.x webpack-cli@4.x
npx webpack --mode=production

Webpack 5.x

npm i --save webpack@5.x webpack-cli@5.x
npx webpack --mode=production

Webpack latest

npm i --save webpack webpack-cli
npx webpack --mode=production
  1. Create a small HTML page that loads the script. Save to index.html:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script src="./index.min.js"></script>
  </body>
</html>
  1. Start a local HTTP server and go to http://localhost:8080/
npx http-server .

Click on "Click here to export" to generate a file.

:::note

The Webpack section of the Content demo covers asset loaders. They are ideal for static sites pulling data from sheets at build time.

:::

WMR

WMR works with no caveats.

Complete Example (click to show)

:::note

This demo was last tested on 2023 May 07 against WMR 3.8.0

:::

  1. Install the tarball using a package manager:
{`\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ pnpm install https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} {`\ yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
  1. Save the following to index.js:
// highlight-next-line
import { utils, version, writeFileXLSX } from 'xlsx';

document.getElementById("xport").addEventListener("click", async() => {
/* fetch JSON data and parse */
const url = "https://sheetjs.com/data/executive.json";
const raw_data = await (await fetch(url)).json();

/* filter for the Presidents */
const prez = raw_data.filter(row => row.terms.some(term => term.type === "prez"));

/* flatten objects */
const rows = prez.map(row => ({
  name: row.name.first + " " + row.name.last,
  birthday: row.bio.birthday
}));

/* generate worksheet and workbook */
const worksheet = utils.json_to_sheet(rows);
const workbook = utils.book_new();
utils.book_append_sheet(workbook, worksheet, "Dates");

/* fix headers */
utils.sheet_add_aoa(worksheet, [["Name", "Birthday"]], { origin: "A1" });

/* calculate column width */
const max_width = rows.reduce((w, r) => Math.max(w, r.name.length), 10);
worksheet["!cols"] = [ { wch: max_width } ];

/* create an XLSX file and try to save to Presidents.xlsx */
writeFileXLSX(workbook, "Presidents.xlsx");
});
  1. Create a small HTML page that loads the script. Save to index.html:
<!DOCTYPE html>
<html lang="en">
  <head></head>
  <body>
    <h1>SheetJS Presidents Demo</h1>
    <button id="xport">Click here to export</button>
    <script type="module" src="./index.js"></script>
  </body>
</html>
  1. Build for production:
npx wmr@3.8.0 build
  1. Start a local HTTP server in dist folder and go to http://localhost:8080/
npx http-server dist/

Click on "Click here to export" to generate a file.