Analytics SDKs reach for window, document, and localStorage during initialization. When that code runs inside a React Server Component or at module top-level in an SSR file, the server throws ReferenceError: window is not defined and the page either 500s or renders a broken shell. Users see a white screen; crawlers see an error; Vercel logs fill with the same stack trace. The analytics tool you installed to observe the site is actively preventing it from rendering.
High because SSR-time crashes take entire pages offline and the fix is small but mandatory.
Move all SDK initialization into a component marked 'use client' and wrap the call in useEffect(() => { initializeAnalytics() }, []) so it runs only after hydration. For script-tag analytics like GA4 or GTM, use Next.js <Script strategy="afterInteractive"> which defers loading until the client is ready. See components/analytics-provider.tsx as the single initialization site.
ID: marketing-analytics.core-analytics.analytics-initialized-client-side
Severity: high
What to look for: In SSR frameworks (Next.js, Nuxt, Remix, SvelteKit), analytics SDKs that access browser globals (window, document, localStorage) must not be initialized during server-side rendering. Look for:
useEffect hooks (correct)typeof window !== 'undefined' guards around analytics initialization (correct)'use client' component (correct) vs. in a Server Component (incorrect)<Script strategy="afterInteractive"> or strategy="lazyOnload" (correct) vs. strategy="beforeInteractive" (acceptable but unusual for analytics)Pass criteria: All analytics initialization code is guarded from running during SSR, either via useEffect, 'use client' directive, typeof window guard, or appropriate <Script> strategy. Count every analytics initialization call and verify each has at least 1 SSR guard.
Fail criteria: Analytics SDK initialization code exists at module top-level in a server-rendered context, OR analytics is called inside a React Server Component without a client guard. Do NOT pass if the 'use client' directive is present on the file but the analytics initialization runs outside a useEffect (top-level module execution in a 'use client' file still runs during SSR in streaming scenarios).
Skip (N/A) when: No analytics is present (script-present failed), OR the project is a purely client-rendered SPA (no SSR), OR analytics is loaded only via external script tags with no SDK initialization code.
Detail on fail: "Analytics initialization found in a Server Component or at module top-level without a window guard. This will throw errors during SSR and may prevent the page from rendering."
Remediation: Move all analytics SDK initialization into a useEffect hook or a component marked with 'use client':
// components/analytics-provider.tsx
'use client'
import { useEffect } from 'react'
export function AnalyticsProvider() {
useEffect(() => {
// Safe to initialize here — runs only in the browser
initializeAnalytics()
}, [])
return null
}
For script-based analytics (GA4, GTM), use Next.js <Script> with strategy="afterInteractive" which automatically handles the SSR/client split.