Performance: Managing Third-Party Script Loading (PERF-24)
Performance: Managing Third-Party Script Loading (PERF-24)
Release: v1.0.70
Control: PERF-24
Category: Performance / Resource Loading
Overview
This audit finding (PERF-24) identifies a risk introduced by a large browser script asset (public/tailwindcss-browser.js, 271KB) whose loading strategy is uncontrolled. Without an explicit loading strategy, this script may load synchronously and block the browser's rendering pipeline, degrading Time to Interactive (TTI) and Largest Contentful Paint (LCP) scores.
No usage of Next.js's built-in next/script component was found in the codebase, indicating that script loading optimisation has not yet been applied to any third-party or large asset script.
The Problem: Render-Blocking Scripts
When a browser encounters a <script> tag in the document without async or defer attributes, it:
- Pauses HTML parsing at that point.
- Downloads the script (network latency applies).
- Executes the script before continuing to parse the rest of the document.
For a 271KB file like tailwindcss-browser.js, this can add hundreds of milliseconds of delay on average connections — directly penalising Core Web Vitals scores and user-perceived load time.
Affected Asset
public/tailwindcss-browser.js
- Size: ~271KB
- Location:
public/directory (served as a static asset) - Suspected use: Dynamic HTML rendering inside the
MarketingIframecomponent - Risk: If loaded via a synchronous
<script>tag on any page, it is render-blocking
Recommended Fixes
Option 1: Use next/script with lazyOnload
If the script is genuinely needed on a page (e.g. for MarketingIframe rendering), replace any bare <script> tag with Next.js's Script component using the lazyOnload strategy. This defers loading until after the page is fully interactive.
import Script from 'next/script';
// Inside your component or page:
<Script
src="/tailwindcss-browser.js"
strategy="lazyOnload"
/>
When to use: The script is needed on the main page but is not critical for initial render.
Option 2: Inject Only into the Iframe Document
If tailwindcss-browser.js is only needed for rendering HTML inside the MarketingIframe, inject it into the iframe's own document rather than the parent page. This confines any performance cost entirely to the iframe context.
// Inside marketing-iframe.tsx — build an srcdoc string
const iframeContent = `
<!DOCTYPE html>
<html>
<head>
<script src="/tailwindcss-browser.js"></script>
</head>
<body>
${dynamicHtmlContent}
</body>
</html>
`;
<iframe srcDoc={iframeContent} />
When to use: The script is only needed to style or render content inside the iframe, never on the main page.
Option 3: Remove the Asset
If tailwindcss-browser.js is not actively referenced anywhere in the application, remove it from public/ entirely.
rm public/tailwindcss-browser.js
Verify no references exist before removing:
grep -r "tailwindcss-browser" ./
When to use: The file is dead code / an unused legacy asset.
Auditing marketing-iframe.tsx
Regardless of which option is chosen for the asset itself, the marketing-iframe.tsx component should be audited to ensure it does not perform any synchronous script injection. Patterns to check for and avoid:
| Pattern | Risk |
|---|---|
document.write('<script ...') | Render-blocking, deprecated |
Setting innerHTML with a <script> tag | Scripts injected via innerHTML may not execute consistently |
Constructing a <script> element and appending to document.head without async/defer | Synchronous execution risk |
Bare <script src="..."> in a JSX return outside of next/script | Bypasses Next.js loading optimisation |
Next.js Script Loading Strategies — Quick Reference
| Strategy | When it loads | Use case |
|---|---|---|
beforeInteractive | Before page hydration | Critical scripts only (e.g. consent management) |
afterInteractive | After page hydration | Analytics, tag managers |
lazyOnload | During browser idle time | Low-priority scripts, marketing tools |
worker (experimental) | In a Web Worker | Offload heavy scripts from main thread |
For most non-critical third-party scripts, lazyOnload or afterInteractive are the appropriate choices.
Related
- Next.js Script Component Documentation
- Changelog: v1.0.70