Why it matters
Client components are still rendered on the server. Touching browser globals at import time or in the render body crashes SSR (window is not defined) or renders different markup on server and client (hydration mismatch, e.g. ⌘ vs Ctrl labels from navigator.platform).
How to fix it
Read browser APIs inside useEffect, event handlers, or useSyncExternalStore with a server snapshot. For values needed in the first paint, render a neutral default and update after mount.
Example
const isMac = useSyncExternalStore(
() => () => {},
() => /Mac/.test(navigator.platform),
() => false, // server snapshot
)References
More lint rules
void lint reports 67 rules in this category. Static source checks with no browser: Tailwind v4 silent failures, Next.js 16 API traps, React render-body bugs, accessibility markup, SEO files and motion hygiene.
google-fonts-linkGoogle Fonts loaded via `<link>` / `@import`font-display`@font-face` without `font-display`too-many-font-familiesMore than 3 font families loadedscript-strategyThird-party script loads too earlytimer-in-render`setTimeout` / `setInterval` called in a component bodymotionvalue-subscribe-in-renderMotionValue `.on("change")` subscription in a component bodywebgl-in-mapWebGL canvas rendered inside `.map()`raf-without-cancel`requestAnimationFrame` loop in an effect without `cancelAnimationFrame`