Sharing JavaScript modules between Node.js and the browser
When building a small project that needs to run both as a command-line tool and inside a web page, the traditional answer has been to reach for a bundler. But with native support for ES modules (ESM) in both Node.js and modern browsers, it's now possible to write modular JavaScript once and load it in both environments without any build step.
To see how this works in practice, consider a project with two core source files that expose their functionality through exported functions. The same files are imported by a test script run under Node.js and by a browser-based application.
Running tests under Node.js
The test file (test/test.js) imports the module functions using relative paths and makes assertions with Node's built-in assert module, with no test framework involved:
import assert from 'node:assert/strict';
import { solve } from '../eqsolve.js';
import { buildSplineEquations } from '../spline.js';
Running the tests requires a recent enough Node.js release that supports ESM natively. After that, the invocation is straightforward:
$ node --version v20.5.0 $ node test/test.js success
Loading the same modules in a browser
Browsers have supported ES modules for several years, so a standard script type="module" tag with import statements will work in any up-to-date browser. The project's main entry point, plot.html, imports functions from eqsolve.js and spline.js just as the Node.js test does, and additionally pulls in the D3 library directly from a URL as an ESM import:
<script type="module">
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
import { buildSplineEquations } from "./spline.js";
import { solve } from "./eqsolve.js";
// ... more web-app JS code here
</script>
One caveat applies to local development: opening plot.html directly via the file:/// scheme will fail with CORS errors, because the browser won't permit imports of local files from such a page. The directory must be served over HTTP. A simple static file server works fine for this purpose:
$ static-server . 2023/10/21 07:07:44.168573 Serving directory "." on http://127.0.0.1:8080
With the server running, the page loads successfully at http://127.0.0.1:8080/plot.html.
Native support for JavaScript imports in the browser represents a meaningful improvement over the previous state of affairs, where structuring a non-trivial web application required external tooling and a build pipeline. The ability to share code between the server-side test environment and the client-side runtime without any intermediate bundling step keeps things noticeably simpler.
| [1] | If you don't have go installed, the NPM http-server package will work just as well. |



