No synchronous render-blocking scripts in `<head>`
Why it matters
A synchronous <script> tag in <head> without async or defer halts the HTML parser entirely until the script downloads, parses, and executes — this is the most severe form of render-blocking resource under ISO-25010 time-behaviour measurement. Google Tag Manager, Hotjar, and Meta Pixel are frequently copied from vendor docs as bare <script src> tags, each adding its full download time as a sequential blocking delay before any page content renders. Two synchronous third-party scripts on a 4G connection can add 400–600ms of blocked parser time, pushing FCP and LCP into 'needs improvement' territory and penalising the page in Google's Page Experience scoring.
Severity rationale
High because a single synchronous script in `<head>` blocks HTML parsing for the duration of its download, adding its full latency directly to FCP and LCP regardless of the script's functional purpose.
Remediation
Replace bare <script> tags with Next.js Script component, specifying the correct strategy for each use case — afterInteractive for analytics and pixels, lazyOnload for non-critical widgets.
import Script from 'next/script'
// Google Tag Manager — fires after hydration
<Script
src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXX"
strategy="afterInteractive"
/>
// Hotjar — can wait until idle
<Script
src="https://static.hotjar.com/c/hotjar-XXXXXX.js"
strategy="lazyOnload"
/>
beforeInteractive is only appropriate for scripts that genuinely need to run before hydration — A/B testing tools sometimes require it for flicker prevention, but verify with the tool's documentation before using it. The default should always be afterInteractive.
Detection
-
ID:
render-blocking-resources -
Severity:
high -
What to look for: Check the
<head>section (root layout, HTML template,_document.tsx) for<script>tags withoutasyncordefer. Any script in<head>without these attributes blocks HTML parsing and rendering. This is the most common mistake with third-party marketing tools: analytics tags, A/B testing scripts, and ad pixels are often pasted directly from vendor documentation which includes synchronous<script>tags. Count every CSS and JavaScript resource in the that blocks rendering. Enumerate each with its URL and size. -
Pass criteria: No
<script>tags in<head>(or before the closing</body>) lackasyncordefer. Next.js Script component is used with appropriate strategy (beforeInteractiveonly when genuinely required,afterInteractiveorlazyOnloadeverywhere else). At least 1 implementation must be confirmed. -
Fail criteria: One or more
<script>tags in<head>are synchronous (noasync, nodefer).beforeInteractivestrategy used for scripts that do not genuinely require it. Do NOT pass if more than 2 render-blocking CSS files are loaded in the without media queries or critical CSS extraction. -
Skip (N/A) when: No
<head>scripts present (no third-party scripts). -
Cross-reference: The
script-loading-strategycheck verifies that JavaScript does not block the critical rendering path alongside CSS. -
Detail on fail:
"2 synchronous <script> tags in root layout <head>: Google Tag Manager and Hotjar snippet pasted verbatim from vendor docs. Both block HTML parsing." -
Remediation: Make every marketing script asynchronous:
import Script from 'next/script' // Google Tag Manager — afterInteractive is correct <Script src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXX" strategy="afterInteractive" /> // A/B testing tools (Optimizely, VWO) — may need beforeInteractive // but verify with the tool's documentation first <Script src="https://cdn.optimizely.com/js/YOUR_ID.js" strategy="beforeInteractive" // Only use this if the tool genuinely requires it for flicker prevention />
External references
- iso-25010:2011 · performance-efficiency.time-behaviour — No render-blocking scripts — reduces time-to-first-paint
Taxons
History
- 2026-04-18·v1.0.0·Initial import from marketing-page-speed·automated