DEV Community

Anas Sheikh
Anas Sheikh

Posted on

That Third-Party Script Tag You Copy-Pasted Is Probably Hurting

Chat widgets, analytics snippets, ad network tags, review platform embeds, nearly all of them ship with a copy-paste <script> snippet meant for a plain HTML page, and pasted directly into a Next.js app without modification, several of them quietly do real damage to your actual performance metrics for content that has nothing to do with the widget itself.

The Copy-Paste That Looks Harmless

// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <script src="https://widget.example.com/embed.js"></script>
      </head>
      <body>{children}</body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

This is exactly the snippet most third-party services hand you, drop this in your <head>, done. It works, the widget shows up. It also, by default, blocks the browser from continuing to parse and render the rest of the page until this script finishes downloading and executing, regardless of whether the widget is something a visitor actually needs immediately, a chat bubble in the corner, or something they'll never notice loading a beat later.

Why This Specifically Matters for Your Core Web Vitals

A plain <script> tag with no special handling is render-blocking by default. For a chat widget, a review platform embed, most analytics tools, none of which are part of the actual content a visitor came to see, this render-blocking behavior directly delays your Largest Contentful Paint and can hurt Time to Interactive, real, measured metrics that affect both user experience and search ranking, all for a script whose own functionality genuinely doesn't need to block anything.

What next/script Actually Solves

Next.js provides next/script specifically to give you real control over when and how a third-party script loads, rather than accepting the plain HTML default of "block everything until this finishes."

import Script from 'next/script';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script src="https://widget.example.com/embed.js" strategy="lazyOnload" />
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

The strategy prop is where the actual decision happens, and choosing the right one for a given script matters far more than just using next/script at all.

The Strategies, and What They're Actually For

beforeInteractive loads and executes before the page becomes interactive, before hydration. This is reserved for genuinely critical scripts the page cannot function correctly without, a polyfill required for the page to render at all, certain bot detection required before any interaction. Using this for a chat widget or analytics script defeats the entire purpose, it's just as render-blocking as the plain, unmodified <script> tag, applied to something that never needed that priority.

afterInteractive (the default when strategy isn't specified) loads after the page becomes interactive, a reasonable default for most analytics and tracking scripts that need to start running reasonably early but don't need to block initial render.

lazyOnload loads during idle browser time, well after everything else has finished, appropriate for genuinely low-priority scripts, a chat widget, a social media embed, anything a visitor doesn't need in the first few seconds and won't notice arriving slightly later.

Where People Get This Wrong

Using the plain, unmodified <script> tag from a provider's documentation, skipping next/script entirely, which defaults to fully render-blocking behavior in every case, since a plain HTML script tag has no strategy concept at all.

Using next/script but leaving every third-party embed on the default afterInteractive strategy, regardless of actual priority. A genuinely low-priority widget, a review platform badge, a social share button set, sitting on afterInteractive still competes for the browser's attention earlier than it needs to, when lazyOnload would serve it identically from the visitor's perspective while doing measurably less damage to earlier, more important metrics.

Setting beforeInteractive on something that isn't actually critical, sometimes copied directly from an example without understanding what that specific strategy is reserved for. This is the most damaging mistake, since it explicitly requests the most aggressive, most blocking loading behavior available, for a script that almost certainly didn't need it.

A Practical Way to Choose the Right Strategy

Ask honestly, does the page genuinely not function correctly without this script running before anything else. If the honest answer is no, and for the overwhelming majority of third-party embeds it is no, beforeInteractive is off the table. Between afterInteractive and lazyOnload, the real question is whether a visitor would notice or care if this specific widget took an extra second or two to actually appear. If not, lazyOnload is almost always the better choice, genuinely no different from the visitor's perspective, meaningfully better for your actual performance metrics.

Checking Your Own Third-Party Scripts

grep -rn "<script" --include="*.tsx" app/
Enter fullscreen mode Exit fullscreen mode

Any raw <script> tag this turns up, not using next/script at all, is a candidate worth converting. For every existing next/script usage, check whether the strategy actually matches the script's real priority, rather than whatever the default happened to be when it was added.

I audit exactly this on client projects and keep it deliberate across the templates I build at pixelanas.com, since a slow Lighthouse score caused entirely by a chat widget nobody consciously chose to prioritize is one of the more avoidable, easy-to-fix performance issues out there.

The Actual Rule

Every third-party script needs a deliberately chosen loading strategy, not whatever a provider's copy-paste snippet or a framework default happens to apply. beforeInteractive for genuinely critical scripts only, afterInteractive for scripts that need to run reasonably early, lazyOnload for everything else, which, in practice, is most third-party embeds most sites actually use.


If you've got third-party scripts loaded as plain <script> tags, or sitting on a strategy nobody actually chose deliberately, worth auditing them against a real Lighthouse report before and after. Drop what you find in the comments.

Get the templates: https://pixelanas.gumroad.com


Anas, full-stack Next.js developer building SaaS products and premium templates. X: @ASheikh69751

Top comments (0)