diff --git a/docz/docs/03-demos/01-frontend/19-bundler/04-esbuild.md b/docz/docs/03-demos/01-frontend/19-bundler/04-esbuild.md
new file mode 100644
index 0000000..2cc331c
--- /dev/null
+++ b/docz/docs/03-demos/01-frontend/19-bundler/04-esbuild.md
@@ -0,0 +1,217 @@
+---
+title: Bundling Sheets with ESBuild
+sidebar_label: ESBuild
+pagination_prev: demos/index
+pagination_next: demos/grid/index
+sidebar_position: 4
+---
+
+import current from '/version.js';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import CodeBlock from '@theme/CodeBlock';
+
+[ESBuild](https://esbuild.github.io/) is a fast module bundler for JavaScript.
+It combines scripts and libraries into simple scripts for browsers and NodeJS.
+
+[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
+data from spreadsheets.
+
+This demo uses ESBuild and SheetJS to export data. We'll explore two workflows:
+
+- ["Browser"](#browser) explores how to import SheetJS libraries in a script and
+bundle with ESBuild for browser use.
+
+- ["NodeJS"](#nodejs) explores how to import SheetJS libraries in a script and
+bundle with ESBuild for NodeJS use.
+
+:::info pass
+
+This demo focuses on integration details with the ESBuild bundler.
+
+The demos follow the ["Export Tutorial"](/docs/getting-started/examples/export).
+The tutorial covers SheetJS library usage.
+
+:::
+
+:::note
+
+This demo was last tested on 2023 October 19 against esbuild `0.19.5`
+
+:::
+
+## Integration Details
+
+[The "Frameworks" section](/docs/getting-started/installation/frameworks) covers
+installation with Yarn and other package managers.
+
+## Browser
+
+ESBuild will bundle the SheetJS ECMAScript Module build:
+
+```js
+import { read, utils, writeFileXLSX } from 'xlsx';
+```
+
+:::note pass
+
+The `xlsx.mjs` source file uses a subset of ES6 that `esbuild` understands and
+is able to transpile for older browsers.
+
+:::
+
+Assuming the primary source file is `in.js`, the following command will bundle
+the script and generate `out.js`:
+
+```bash
+npx -y esbuild@0.19.5 in.js --bundle --outfile=out.js
+```
+
+### Browser Demo
+
+0) Prepare a blank project:
+
+```bash
+mkdir sheetjs-esbrowser
+cd sheetjs-esbrowser
+npm init -y
+```
+
+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`}
+
+  
+
+
+2) Download [`esbrowser.js`](pathname:///esbuild/esbrowser.js) and move to the
+project folder:
+
+```bash
+curl -LO https://docs.sheetjs.com/esbuild/esbrowser.js
+```
+
+3) Create a small HTML page that loads the script.  Save to `index.html`:
+
+```html title="index.html"
+
+```
+
+4) Create bundle:
+
+```bash
+npx -y esbuild@0.19.5 esbrowser.js --bundle --outfile=esb.browser.js
+```
+
+5) Start a local HTTP server:
+
+```bash
+npx http-server .
+```
+
+Access the displayed URL (typically `http://localhost:8080`) with a web browser.
+It should attempt to download `Presidents.xlsx`
+
+## NodeJS
+
+ESBuild will bundle the SheetJS ECMAScript Module build:
+
+```js
+import { read, utils, write } from 'xlsx';
+```
+
+:::caution pass
+
+To read and write files on the local filesystem using the SheetJS `readFile` and
+`writeFile` methods[^1], the `fs` module must be manually added:
+
+```js
+import { set_fs, readFile } from 'xlsx';
+import * as fs from 'fs';
+set_fs(fs);
+
+/* read pres.numbers in the same directory as the script */
+const wb = readFile("pres.numbers");
+```
+
+:::
+
+Assuming the primary source file is `in.js`, the following command will bundle
+the script for NodeJS and generate `out.js`:
+
+```bash
+npx -y esbuild@0.19.5 in.js --bundle --platform=node --outfile=out.js
+```
+
+### NodeJS Demo
+
+:::info pass
+
+This demo script uses `fetch` and requires Node 18+.
+
+:::
+
+0) Prepare a blank project:
+
+```bash
+mkdir sheetjs-esbnode
+cd sheetjs-esbnode
+npm init -y
+```
+
+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`}
+
+  
+
+
+2) Download [`esbnode.js`](pathname:///esbuild/esbnode.js) and move to the
+project folder:
+
+```bash
+curl -LO https://docs.sheetjs.com/esbuild/esbnode.js
+```
+
+3) Create bundle:
+
+```bash
+npx -y esbuild@0.19.5 esbnode.js --bundle --platform=node --outfile=esb.node.js
+```
+
+4) Run the bundle:
+
+```bash
+node esb.node.js
+```
+
+The process will generate `Presidents.xlsx` in the project directory. Open the
+file in a spreadsheet editor.
+
+[^1]: The SheetJS [`readFile`](/docs/api/parse-options) and [`writeFile`](/docs/api/write-options) methods use the NodeJS `fs` module when available. It is not automatically loaded in the ECMAScript Module builds.
\ No newline at end of file
diff --git a/docz/docs/03-demos/01-frontend/19-bundler/12-systemjs.md b/docz/docs/03-demos/01-frontend/19-bundler/12-systemjs.md
index c6b4e1a..78c0429 100644
--- a/docz/docs/03-demos/01-frontend/19-bundler/12-systemjs.md
+++ b/docz/docs/03-demos/01-frontend/19-bundler/12-systemjs.md
@@ -111,7 +111,9 @@ _cb = function(evt) { /* ... do work here ... */ };
 
 :::caution pass
 
-While SystemJS works in NodeJS, the built-in `require` should be preferred.
+**It is strongly recommended to use the NodeJS `require` method when possible.**
+
+This demo is relevant for legacy projects that use the SystemJS NodeJS loader.
 
 :::
 
diff --git a/docz/docs/03-demos/01-frontend/19-bundler/index.md b/docz/docs/03-demos/01-frontend/19-bundler/index.md
index c64a92b..d6e9d0c 100644
--- a/docz/docs/03-demos/01-frontend/19-bundler/index.md
+++ b/docz/docs/03-demos/01-frontend/19-bundler/index.md
@@ -209,187 +209,9 @@ Integration details are included [in the "AMD" installation](/docs/getting-start
 
 Complete Examples are included [in the "Dojo" demo](/docs/demos/frontend/legacy#dojo-toolkit)
 
+#### esbuild
 
-## 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`}
-
-  
-
-
-2) Save the following to `esbrowser.js`:
-
-```js title="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");
-})();
-```
-
-3) Create a small HTML page that loads the script.  Save to `index.html`:
-
-```html title="index.html"
-
-```
-
-4) Create bundle:
-
-```bash
-npx esbuild@0.17.18 esbrowser.js --bundle --outfile=esb.browser.js
-```
-
-5) Start a local HTTP server, then go to `http://localhost:8080/`
-
-```bash
-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`}
-
-  
-
-
-2) Save the following to `esbnode.js`:
-
-```js title="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");
-})();
-```
-
-3) Create bundle:
-
-```bash
-npx esbuild@0.17.18 esbnode.js --bundle --platform=node --outfile=esb.node.js
-```
-
-4) Run the bundle:
-
-```bash
-node esb.node.js
-```
-
-  
-
-
-