Writing npm packages that support both Node.js and the browser

Vinson Chuong
Scripting Bits
Published in
2 min readJul 19, 2020

Nowadays, it’s important, when writing a new package, to ask where the code should be able to run. With many popular packages supporting both environments as well as the prevalence of compilers, the cost-benefit of supporting both environments is pretty good.

Today, I’m taking another look at how it’s done today.

In the early days of bundling/compilation, developers would try to force browser compatibility by providing mock implementations of Node.js features. At around the same time, jsdom was porting browser features over to Node.js.

And, of course, there were packages like Lodash that don’t rely on any environment-specific features.

Over time, the most useful environment-specific features emerged and were encapsulated into packages that supported both environments. Packages like isomorphic-fetch can be run in both Node.js and the browser, and, packages that use it inherit that cross-runtime support.

But, how are cross-runtime packages implemented in the first place?

I took a look at the code for a few packages. The goal is to have different code execute for different environments. Two implementations of this are:

  • Use conditionals to differentiate between environments at runtime. E.g., looking for window.
  • Configure compiler/bundlers to use different code files for the browser. It turns out that all of the bundlers support the browser field in package.json.

I like using conditionals whenever possible because it keeps my code more cohesive and makes fewer assumptions about tooling. The main challenge with this approach is in importing code from the Node.js standard library, which doesn’t exist in the browser. import statements cannot be made conditional. import() expressions can, but they’re asynchronous.

A lot of the time, I use the browser field to have a different main file for Node.js and the browser. So, when a user imports the package, I can export different things depending on the environment.

--

--