Skip to content

colocohen/js-runtime-environment

Repository files navigation

js-runtime-environment

Get where your JavaScript runs.

One import. Zero dependencies. ~6 KB minified. Full TypeScript support.
Detect Browser, Node.js, Bun, Deno, Electron, Workers, Edge runtimes, and more — reliably.

import { isBrowser, isNode } from 'js-runtime-environment';

if (isBrowser()) {
  // do browser things
} else if (isNode()) {
  // do server things
}

Why?

You have code that runs in more than one environment — a utility library, an SDK, an isomorphic app. So you end up writing this everywhere:

// ❌ Fragile, repetitive, incomplete
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
  // probably a browser… but what about JsDom? Electron renderer?
} else if (typeof process !== 'undefined' && process.versions?.node) {
  // probably Node… but what about Bun? Deno?
} else if (typeof self !== 'undefined' && typeof importScripts === 'function') {
  // probably a worker… but Service Worker or Web Worker?
}

Every new file, every new module — the same copy-pasted checks. They're brittle, easy to get wrong, and never cover all the edge cases.

js-runtime-environment replaces all of that with a single import:

// ✅ One import at the top — that's it
import { isBrowser, isNode, isServiceWorker, run_env } from 'js-runtime-environment';

Add it once at the top of any shared file and you're done. No more boilerplate, no more guessing.

Installation

npm i js-runtime-environment

Works with npm, yarn, pnpm, and bun.

Quick Start — Pick Your Style

The library works however you like to write JavaScript:

ES Modules

import { run_env, isBrowser, isNode } from 'js-runtime-environment';

CommonJS

const { run_env, isBrowser, isNode } = require('js-runtime-environment');

Browser (script tag)

<script src="js-runtime-environment.min.js"></script>
<script>
  console.log(run_env.name);  // "browser"
  console.log(isBrowser());   // true
</script>

Web Worker

importScripts('js-runtime-environment.min.js');
console.log(run_env.name);  // "web-worker"

AMD (RequireJS)

require(['js-runtime-environment'], function (env) {
  console.log(env.run_env.name);
});

Real-World Examples

Here are a few common patterns where this library saves you time:

Isomorphic data fetching — use fetch in the browser, node:fs on the server:

import { isBrowser } from 'js-runtime-environment';

export async function loadConfig() {
  if (isBrowser()) {
    return (await fetch('/config.json')).json();
  }
  const fs = await import('fs/promises');
  return JSON.parse(await fs.readFile('./config.json', 'utf8'));
}

Worker-aware logging — tag logs so you know where they came from:

import { run_env } from 'js-runtime-environment';

function log(msg) {
  console.log(`[${run_env.name}] ${msg}`);
  // → "[browser] loaded"  or  "[service-worker] cache miss"  or  "[node] server started"
}

Environment-aware config — adjust behavior for dev vs production:

import { isDevelopment, isProduction, isCI } from 'js-runtime-environment';

const config = {
  logLevel: isDevelopment() ? 'debug' : 'error',
  sourceMaps: !isProduction(),
  skipTests: isCI() ? false : true,
};

Platform-aware storage — pick the right persistence layer:

import { isBrowser, isNode, isElectron } from 'js-runtime-environment';

function getStorage() {
  if (isElectron()) return require('electron-store');
  if (isBrowser())  return localStorage;
  if (isNode())     return new (require('conf'))();
}

What It Detects

Category Environments
Browsers Browser (real), JsDom
Workers Web Worker, Service Worker, Worklet, Dedicated Worker, Shared Worker
Server runtimes Node.js, Node.js Worker, Bun, Deno
Desktop shells Electron (main / renderer), NW.js
Mobile / Hybrid React-Native
Edge runtimes Vercel Edge, Cloudflare Workers, Netlify, Fastly
OS (best-effort) macOS, Windows, Linux, iOS, Android
Environment development, production, test, CI

API

run_env — the full picture

A snapshot of everything detected at load time. Check run_env.name for a quick string identifier, or use the boolean flags for more detailed logic:

run_env.name              // "browser", "node", "bun", "deno", "jsdom",
                          // "web-worker", "service-worker", "worklet",
                          // "electron-main", "electron-renderer", "nwjs",
                          // "react-native", "edge-runtime", "cloudflare-worker",
                          // "netlify", "fastly", "node-worker", "shell", "unknown"

// Boolean flags
run_env.web               // window + document exist
run_env.window            // window exists
run_env.jsdom             // running inside JsDom (not a real browser)
run_env.worker            // any worker (Web / Service / Worklet)
run_env.web_worker        // Web Worker only (not SW, not Worklet)
run_env.service_worker
run_env.worklet
run_env.nodejs
run_env.node_main         // Node.js main thread
run_env.node_worker       // Node.js worker_threads
run_env.bun
run_env.deno
run_env.electron
run_env.electron_main
run_env.electron_renderer
run_env.nwjs
run_env.react_native
run_env.edge_runtime      // Vercel Edge
run_env.cloudflare_worker
run_env.netlify
run_env.fastly
run_env.shell             // nothing else matched

// Version strings (null when not applicable)
run_env.details.node      // e.g. "20.11.0"
run_env.details.bun       // e.g. "1.0.25"
run_env.details.deno      // e.g. "1.38.0"
run_env.details.electron  // e.g. "28.0.0"
run_env.details.edge      // e.g. "edge-runtime"

Helper functions

Simple boolean functions you can call safely in any runtime:

// Runtime
isBrowser()          // true only in a real browser (not JsDom)
isNode()
isBun()
isDeno()
isElectron()
isJsDom()

// Workers — isWebWorker() returns true for ANY worker type.
// Use the specific helpers below for fine-grained detection.
isWebWorker()
isDedicatedWorker()
isSharedWorker()
isServiceWorker()

// OS (best-effort)
isMacOs()
isWindows()
isLinux()
isIos()
isAndroid()

// Environment
isDevelopment()      // NODE_ENV === "development" or "dev"
isProduction()       // NODE_ENV === "production"
isTest()             // NODE_ENV === "test" or TEST env var is set
isCI()               // detects GitHub Actions, GitLab CI, CircleCI, Travis,
                     // Jenkins, CodeBuild, Bitbucket, Azure DevOps, TeamCity, Buildkite

// Utilities
hasTTY()             // stdout attached to a TTY
isColorSupported()   // terminal supports color output
nodeVersion          // e.g. "20.11.0" or null
nodeMajorVersion     // e.g. 20 or null

TypeScript

Full type definitions are included out of the box. No need for @types/*:

import { run_env, isBrowser, RunEnv } from 'js-runtime-environment';

function doSomething(env: RunEnv) {
  if (env.nodejs) { /* ... */ }
}

How It Works

No User-Agent sniffing for runtime detection. The library uses feature detection with safe try/catch guards:

Environment How it's detected
Service Worker skipWaiting + clients on self
Web Worker importScripts without Window
Worklet WorkletGlobalScope
JsDom window + document exist, UA contains jsdom
Node main vs worker worker_threads.isMainThread
Electron process.versions.electron + window presence
Bun / Deno Detected before browser — never misidentified when they polyfill window
Cloudflare Workers WebSocketPair / HTMLRewriter globals
Netlify NETLIFY environment variable
Fastly globalThis.fastly global
Vercel Edge EdgeRuntime global
CI Checks env vars for 12+ known CI providers
OS navigator.userAgentData.platform, navigator.platform, process.platform

Build & Test

npm run build     # → js-runtime-environment.min.js
npm test          # → runs test suite (62 tests)

Comparison With Other Libraries

The two most popular alternatives are environment by sindresorhus (~19M weekly downloads, ESM-only) and std-env by unjs (~5M weekly downloads, focused on server/CI). Here's how they compare:

Feature js-runtime-environment environment std-env
Module formats CJS + ESM + UMD + AMD ESM only CJS + ESM
TypeScript types
Browser
JsDom ✅ (flag + name)
Web Worker
Service Worker
Dedicated / Shared Worker
Worklet
Node.js
Node.js main / worker split
Bun
Deno
Electron
Electron main / renderer split
NW.js
React Native
Cloudflare Workers ✅ (workerd)
Vercel Edge ✅ (edge-light)
Netlify
Fastly
OS detection ✅ (partial)
isDevelopment / isProduction
CI detection
hasTTY / color support
Node version
run_env snapshot object
Script tag / global usage

In short: environment covers browser/worker/desktop well but is ESM-only with no edge runtimes or env flags. std-env is strong on server runtimes and CI/DevOps but doesn't detect browsers, workers, or desktop shells. This library combines both worlds — plus coverage for Electron main/renderer, Node.js worker threads, NW.js, React Native, and Worklets that neither of them offers.

Contributing

Issues and PRs are welcome! Areas where help is especially useful:

  • Additional edge-platform detectors
  • Smoke tests across browsers (Service Worker, Web Worker, Worklet)
  • Accuracy improvements for React-Native and desktop shells

License

MIT

About

Get where your JavaScript runs - Universal runtime detection for Browser, Node.js, Deno, Bun, Electron and more

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors