correctness/instance-browser-global · Browser global during component initialisation
A component's instance script runs on the server during SSR, so window/document reads at its top level crash the render.
Severity: warning · Category: correctness
What it checks
Flags reads of browser-only globals (the same list as correctness/server-browser-global) at the top level of a component’s <script>, since that code runs on the server on every SSR render of the component. The same guards apply: browser from $app/environment, typeof checks (early-return guards included), onMount/$effect bodies, your own bindings, and shadowed locals are never flagged.
Why it matters
const width = window.innerWidth; at the top of a component works in the browser and in a client-only dev flow, then crashes the first SSR render with ReferenceError: window is not defined.
This is a warning, not critical: a component that is only ever rendered behind a parent’s {#if browser} (or dynamically imported on the client) legitimately never runs on the server, and that cannot be proven from the component file alone. If that is your case, add // svelte-vitals-disable-next-line correctness/instance-browser-global above the line.
A browser global used only as a $props() destructuring default (let { width = window.innerWidth } = $props()) is not flagged either. The default only evaluates when the prop is absent, including during SSR, but the scanner doesn’t visit destructuring defaults. This is a silent conservative miss, not a guard.
How to fix
<script>
const width = window.innerWidth; // ❌ crashes SSR
</script>
The preferred modern form for a window property is svelte/reactivity/window (5.11.0+): no guard needed, undefined on the server, reactive on the client:
<script>
import { innerWidth } from 'svelte/reactivity/window';
</script>
<p>{innerWidth.current}</p>
For anything svelte/reactivity/window doesn’t cover, read the global in onMount, which never runs on the server:
<script>
import { onMount } from 'svelte';
let width2 = $state(0);
onMount(() => {
width2 = window.innerWidth; // ✅ onMount never runs on the server
});
</script>
Mode differences
None. This rule reads source, the same .svelte and .ts files, everywhere it runs. The CLI, the Vite plugin’s build pass, and the live dashboard’s static baseline all report it identically, and the rendered-HTML pass never re-evaluates it. Scoping a run with --route skips it: component-scoped rules have no route to attribute a finding to.
Disabling
Silence a single occurrence with <!-- svelte-vitals-disable-next-line correctness/instance-browser-global --> on the line above it, or turn the rule off:
export default {
rules: {
'correctness/instance-browser-global': 'off'
}
};