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
}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.
npm i js-runtime-environmentWorks with npm, yarn, pnpm, and bun.
The library works however you like to write JavaScript:
import { run_env, isBrowser, isNode } from 'js-runtime-environment';const { run_env, isBrowser, isNode } = require('js-runtime-environment');<script src="js-runtime-environment.min.js"></script>
<script>
console.log(run_env.name); // "browser"
console.log(isBrowser()); // true
</script>importScripts('js-runtime-environment.min.js');
console.log(run_env.name); // "web-worker"require(['js-runtime-environment'], function (env) {
console.log(env.run_env.name);
});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'))();
}| 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 |
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"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 nullFull 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) { /* ... */ }
}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 |
npm run build # → js-runtime-environment.min.js
npm test # → runs test suite (62 tests)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.
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
MIT