Skip to main content

no-dom-globals-in-module-scope

Disallow browser-only globals during module evaluation.

Targeted pattern scope

This rule targets eager browser-global references in module bodies, static fields, static blocks, and immediately invoked functions.

What this rule reports

It reports unguarded references to browser-only globals that execute while a module is imported.

Why this rule exists

Module bodies, static fields, and static initialization blocks execute while a server imports a module. Accessing window, document, or another browser-only global there can make server-side rendering fail before any component renders.

❌ Incorrect

const title = document.title;

class Viewport {
static width = window.innerWidth;
}

✅ Correct

const readTitle = () => document.title;

if (typeof window !== "undefined") {
window.addEventListener("load", initialize);
}

const hasDocument = "document" in globalThis;
if (hasDocument) {
globalThis.document.title = "Ready";
}

Immediately invoked functions inherit module execution and are checked. Deferred function bodies, instance field initializers, type-only references, shadowed locals, and statically recognizable availability guards are not reported.

Availability analysis recognizes direct and globalThis typeof comparisons, static property checks such as "document" in globalThis, and those predicates stored in a single const binding. Mutable predicates and shadowed globalThis bindings are not trusted. Scope, guard, JSX, and execution results are cached per linted file so repeated references do not repeat whole-tree work.

Both direct references and static globalThis properties are checked. The browser-global set comes from the current globals package with Node globals removed.

Behavior and migration notes

Review each finding manually and move the access behind an availability guard or client-only entry point; this rule does not autofix.

ESLint flat config example

import etcMisc from "eslint-plugin-etc-misc";

export default [
{
plugins: { "etc-misc": etcMisc },
rules: { "etc-misc/no-dom-globals-in-module-scope": "error" },
},
];

When not to use it

Disable this rule in browser-only entry points that are never imported by a server. The rule reports only because safe deferral depends on application architecture.

Package documentation

Rule catalog ID: R032

Further reading

This is a modern lexical-scope rewrite inspired by eslint-plugin-ssr-friendly.

Adoption resources

  • Start at warning level in CI, then move to error after cleanup.
  • Review each finding against the code's SSR execution path before changing it.