---
title: "Browser global (`window`, `document`, `navigator`, `localStorage`) at module scope or during render (lint/browser-global-in-render)"
description: "lint/browser-global-in-render: Client components are still rendered on the server."
canonical: https://void-design.vercel.app/rules/lint/browser-global-in-render
lastModified: 2026-09-16
---

# Browser global (`window`, `document`, `navigator`, `localStorage`) at module scope or during render

`lint/browser-global-in-render` · severity **error** · category Lint · detected by `void lint` and `void audit`

## 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

```tsx
const isMac = useSyncExternalStore(
  () => () => {},
  () => /Mac/.test(navigator.platform),
  () => false, // server snapshot
)
```

## References

- https://react.dev/reference/react/useSyncExternalStore#adding-support-for-server-rendering
- https://nextjs.org/docs/messages/react-hydration-error

## 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-link` Google Fonts loaded via `<link>` / `@import` — [lint/google-fonts-link](https://void-design.vercel.app/rules/lint/google-fonts-link)
- `font-display` `@font-face` without `font-display` — [lint/font-display](https://void-design.vercel.app/rules/lint/font-display)
- `too-many-font-families` More than 3 font families loaded — [lint/too-many-font-families](https://void-design.vercel.app/rules/lint/too-many-font-families)
- `script-strategy` Third-party script loads too early — [lint/script-strategy](https://void-design.vercel.app/rules/lint/script-strategy)
- `timer-in-render` `setTimeout` / `setInterval` called in a component body — [lint/timer-in-render](https://void-design.vercel.app/rules/lint/timer-in-render)
- `motionvalue-subscribe-in-render` MotionValue `.on("change")` subscription in a component body — [lint/motionvalue-subscribe-in-render](https://void-design.vercel.app/rules/lint/motionvalue-subscribe-in-render)
- `webgl-in-map` WebGL canvas rendered inside `.map()` — [lint/webgl-in-map](https://void-design.vercel.app/rules/lint/webgl-in-map)
- `raf-without-cancel` `requestAnimationFrame` loop in an effect without `cancelAnimationFrame` — [lint/raf-without-cancel](https://void-design.vercel.app/rules/lint/raf-without-cancel)

Detected by `void lint` and `void audit`. Explain it in a terminal: `void rules lint/browser-global-in-render`
