How to Use `beforeInteractive` Effectively in Next.js

You've carefully built your Next.js application with optimized components and efficient routing, but when you try to add that crucial third-party script, everything falls apart. Your scripts aren't loading at the right time, or worse, they're being transformed into something entirely different in the DOM.

"I've spent days trying to figure out how to add this synchronous script tag to my Next.js project... when I add the suggested <Script> component from next/script it ends up adding a completely different element to the DOM." - frustrated developer on Reddit

If you're nodding along, you're not alone. The beforeInteractive strategy in Next.js promises to solve these problems by loading scripts early, but using it effectively requires understanding its nuances and limitations.

What is the beforeInteractive Strategy?

The beforeInteractive strategy is one of several script loading strategies provided by Next.js through its next/script component. When you specify a script with this strategy, Next.js will:

  • Inject the script into the initial HTML sent from the server

  • Download it before any Next.js code runs

  • Execute it before page hydration occurs

This makes it ideal for scripts that need to be available immediately when a user interacts with your site, such as bot detectors, cookie consent managers, or critical theme initializers.

Here's what the implementation looks like:

import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'

export default function Document() {
    return (
        <Html>
            <Head />
            <body>
                <Main />
                <NextScript />
                <Script
                    src="https://example.com/critical-script.js"
                    strategy="beforeInteractive"
                />
            </body>
        </Html>
    )
}

It's important to note that beforeInteractive scripts must be placed inside the Document Component (pages/_document.js or pages/_document.tsx). This placement requirement exists because these scripts need to be included in the initial server-rendered HTML.

When to Use beforeInteractive

The beforeInteractive strategy shines in specific scenarios:

  1. Cookie consent managers that need to block cookies before any tracking occurs

  2. Bot detection services that should run before user interaction

  3. Critical theme initializers to prevent Flash of Unstyled Content (FOUC)

  4. Security scripts that need to validate the user or session early

For everything else, consider using alternative strategies like afterInteractive (the default) or lazyOnload.

Advantages of Using beforeInteractive

1. Immediate Execution

The most significant advantage of beforeInteractive is that it ensures your scripts execute before the page becomes interactive. This is crucial for functionality that needs to be in place from the moment a user can interact with your site.

For example, if you're implementing a dark mode toggle, you want to apply the user's preference immediately to avoid a jarring flash of white background before switching to dark mode:

// In _document.js
<Script
  id="theme-script"
  strategy="beforeInteractive"
  dangerouslySetInnerHTML={{
    __html: `
      // Check for saved theme preference or prefer-color-scheme
      (function() {
        if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
          document.documentElement.classList.add('dark');
        } else {
          document.documentElement.classList.remove('dark');
        }
      })();
    `,
  }}
/>

2. Server-side Injection

Since beforeInteractive scripts are injected at the server level, they're already present in the initial HTML when it reaches the browser. This means they don't depend on client-side JavaScript to load, which can be beneficial for:

  • Users with slower JavaScript execution

  • Scenarios where you need functionality before React hydration

  • Reducing layout shifts caused by late-loading scripts

3. Performance Optimization for Critical Scripts

By selectively using beforeInteractive for only the most critical scripts, you can ensure these resources are prioritized appropriately in the loading sequence. This can lead to better perceived performance, as essential functionality is available sooner.

Limitations and Challenges

While beforeInteractive is powerful, it comes with significant limitations you need to be aware of:

1. Transformation of Script Tags

Many developers are frustrated when their carefully crafted script tags get modified by Next.js:

"I don't want to 'preload' anything, I don't want 'async' scripts. The original script in its original form must be added to the head." - Reddit user

Next.js transforms script tags to optimize loading, which can break scripts that rely on specific attributes or synchronous execution. This is particularly problematic for legacy scripts that weren't designed with modern loading strategies in mind.

2. Execution Timing Issues

Despite the name, beforeInteractive doesn't guarantee that your script will execute before specific DOM elements are available:

"The problem is that this script doesn't get called at all since I can't see 'initTheme' in the console." - Developer trying to prevent FOUC

Scripts might not run exactly when you expect, leading to timing issues, especially with scripts that need to manipulate the DOM early.

3. SEO Implications

Using beforeInteractive incorrectly can lead to SEO problems:

"I'm working on this ecommerce site that has canonicals and hreflang in the strategy='beforeInteractive' JS tag and details look like missing on pages some times when load DOM." - SEO specialist on Reddit

Critical SEO elements like canonical tags and hreflang attributes might be missing if they depend on scripts that don't execute properly.

Best Practices for Using beforeInteractive

To avoid the common pitfalls and leverage the advantages of beforeInteractive, follow these best practices:

1. Use Sparingly and Strategically

Reserve beforeInteractive for truly critical scripts only. Every script loaded with this strategy adds to the initial payload and can potentially delay rendering. Ask yourself:

  • Does this script need to run before the user can interact with the page?

  • Will the user experience be significantly degraded if this script loads later?

If the answer to either question is "no," consider using afterInteractive (the default) or lazyOnload instead.

2. Implement Error Handling

Always include error handling for your critical scripts to prevent catastrophic failures:

<Script
    src="https://example.com/critical-script.js"
    strategy="beforeInteractive"
    onError={(e) => {
        console.error('Critical script failed to load', e);
        // Implement fallback behavior
    }}
/>

This ensures that even if your critical script fails, the user experience isn't completely broken.

3. Consider Alternative Approaches for DOM Manipulation

If you're using beforeInteractive primarily for early DOM manipulation (like setting a theme class), consider these alternatives:

Option 1: Inline Script in Head

For the most reliable early execution, you can use a regular script tag directly in your _document.js:

// In _document.js
export default function Document() {
  return (
    <Html>
      <Head>
        {/* This will execute very early */}
        <script dangerouslySetInnerHTML={{
          __html: `
            (function() {
              if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
                document.documentElement.classList.add('dark');
              } else {
                document.documentElement.classList.remove('dark');
              }
            })();
          `
        }} />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

Option 2: Client Component with useEffect

For theme initialization that can wait for component mounting:

'use client'

import { useEffect } from 'react'

export function ThemeInitializer() {
  useEffect(() => {
    if (localStorage.theme === 'dark' || 
        (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
      document.documentElement.classList.add('dark');
    } else {
      document.documentElement.classList.remove('dark');
    }
  }, []);
  
  return null;
}

4. Place Global Scripts in _document.js

For scripts that need to be available on every page and load early, place them in your _document.js file:

"Whatever you wanna load on the entire site as beforeInteractive, use it on _document.js." - SEO specialist recommendation

This ensures consistent loading across your entire application and helps prevent issues with missing elements like canonical tags.

5. Consider Partytown for Third-Party Scripts

For third-party scripts that are important but not critical for the initial user experience, consider using Partytown, which runs scripts in web workers:

import { Partytown } from '@builder.io/partytown/react';

// In your _document.js
<Head>
  <Partytown />
  {/* Other third-party scripts */}
</Head>

This approach can give you the benefits of early loading without the performance penalties.

Real-World Examples

Example 1: Cookie Consent Manager

// In _document.js
<Script
  src="https://cdn.cookielaw.org/consent-manager.js"
  strategy="beforeInteractive"
  id="cookie-consent"
  onError={(e) => console.error('Cookie consent failed to load', e)}
/>

Example 2: Bot Detection

// In _document.js
<Script
  strategy="beforeInteractive"
  src="https://cdn.botdetection.service/detect.js"
  id="bot-detection"
/>

Conclusion

The beforeInteractive strategy in Next.js is a powerful tool for loading critical scripts early, but it requires careful implementation. By understanding its advantages and limitations, you can make informed decisions about when to use it and when to opt for alternatives.

Remember these key points:

  • Use beforeInteractive only for truly critical scripts

  • Be aware of potential DOM manipulation timing issues

  • Consider direct script tags for the most reliable early execution

  • Always implement error handling

  • Place global scripts in _document.js

By following these guidelines, you can leverage the power of beforeInteractive while avoiding common pitfalls, ensuring your Next.js application provides an optimal user experience from the moment it loads.

For more information, refer to the official Next.js documentation on the Script component or explore optimization techniques in the Chrome Developers guide to Next.js script loading.

Next.js Logo

6/19/2025
Related Posts
Lazy Loading in Next.js: The Future of Resource Optimization

Lazy Loading in Next.js: The Future of Resource Optimization

Struggling with high TBT scores? Discover how to master Next.js lazy loading to optimize React's hydration process and stop your components from killing your Lighthouse performance.

Read Full Story
SEO Meets Performance: Optimizing Next.js Without Losing Rankings

SEO Meets Performance: Optimizing Next.js Without Losing Rankings

Struggling with slow Next.js apps and falling SEO rankings? Learn how to optimize React's hydration process and implement lazy loading without sacrificing your search visibility.

Read Full Story
Understanding React's Hydration Process: The Missing Link to Enhanced Performance

Understanding React's Hydration Process: The Missing Link to Enhanced Performance

Tired of poor TBT scores and unresponsive buttons after page load? Dive deep into React's hydration process and discover expert techniques to transform your Next.js app's performance.

Read Full Story