Knowledge Base/Optimizing Script Loading in Next.js: A Performance-First Approach

Optimizing Script Loading in Next.js: A Performance-First Approach

how-toExperience ArchitectPerformanceAnalytics

Introduction

There are multiple strategies when it comes to offloading third-party scripts (tag managers, analytics and tracking scripts) and while Next.js has a built-in support for third-party scripts, sometimes you may need a more creative approach to battle the problem when the standard approaches don't cut it.

In this article, we'll explore how to implement delayed script loading in Next.js while considering user interactions and GDPR compliance.

Key Benefits

  • 🚀 Improved Core Web Vitals
  • ⚡️ Faster initial page load
  • 🎯 Better user experience
  • 🔒 GDPR compliance
  • 📊 Accurate analytics tracking

⚠️ Important considerations

Make sure to align this approach with marketing team and discuss pros & cons. This approach is not a standard way of loading third-party scripts. If you need to prioritize performance and all other options of third-party script optimization fall short. Our marketing team implemented it successfully for more than a year, but your mileage may vary.

Implementation Steps

1. Create a User Interaction Hook

First, create a hook to detect when users start interacting with your website. This helps delay script loading until the user actually engages with the page.

// useUserStartInteractions.ts const useUserStartInteractions = (): boolean => { const [isUserInteractionStarted, setIsUserInteractionStarted] = useState(false); const listener = useCallback(() => setIsUserInteractionStarted(true), []); useEffect(() => { if (isUserInteractionStarted) return; // Listen for user interactions window.addEventListener('mousemove', listener, { passive: true, once: true }); window.addEventListener('touchmove', listener, { passive: true, once: true }); // Fallback timeout after 5 seconds const id = setTimeout(() => { listener(); // Cleanup listeners window.removeEventListener('mousemove', listener); window.removeEventListener('touchmove', listener); }, 5000); return () => { clearTimeout(id); window.removeEventListener('mousemove', listener); window.removeEventListener('touchmove', listener); }; }, [isUserInteractionStarted, listener]); return isUserInteractionStarted; };

2. Implement Cookie Policy Management

Advanced: Custom Storage Hook (Optional)

For better state management across components and browser storage synchronization, you can implement a custom storage hook:

// useStorage.ts function useStorage<T>( key: string, initialValue: T, storage: StorageName = StorageName.LOCAL_STORAGE ): [T, SetValue<T>] { // Initialize state with value from storage or initial value const [value, setStoredValue] = useState<T>(getStorageItem(key, storage) || initialValue); // Create event listener to sync state across tabs/windows const listener = useCallback( e => { const nextValue = getStorageItem(e.type, storage); setStoredValue(nextValue as T); }, [storage] ); // Set up event listeners useEffect(() => { window.addEventListener(key, listener, false); return () => { window.removeEventListener(key, listener); }; }, [key, listener]); // Create setter function const setValue: SetValue<T> = useCallback( nextValue => { const valueToStore = nextValue instanceof Function ? nextValue(value) : nextValue; setStorageItem({ [key]: valueToStore }, storage); // Dispatch event to notify other components window.dispatchEvent(new Event(key)); }, [key, value, storage] ); return [value, setValue]; }

For GDPR compliance, create a hook to manage cookie consent:

// useCookiesPolicy.tsx const useCookiesPolicy = () => { const [cookiesPolicy, setCookiesPolicy] = useStorage(StorageKeys.COOKIES_POLICY, { allow: false, timestamp: '', }); const isExpired = useMemo(() => { if (!cookiesPolicy.timestamp) return true; const date = new Date(); const expires = new Date(cookiesPolicy.timestamp); return date.getTime() > expires.getTime(); }, [cookiesPolicy.timestamp]); return { isAllowed: cookiesPolicy.allow, isExpired, setCookiesPolicy, }; };

3. Create a Trackers Provider Component

The main component that manages script loading:

// TrackersProvider.tsx const TrackersProvider: React.FC<Required<Props>> = memo(({ hubspotPortalId, isGDPRCountry, gtmId }) => { const isUserInteractionStarted = useUserStartInteractions(); const { isAllowed, isExpired } = useCookiesPolicy(); // Don't render scripts if: // 1. User hasn't interacted with the page // 2. User is from GDPR country and hasn't given consent if (!isUserInteractionStarted || (isGDPRCountry && (!isAllowed || isExpired))) { return null; } return ( <> {/* Load scripts with afterInteractive strategy */} {hubspotPortalId && <Script src="/scripts/hubspot.js" strategy="afterInteractive" />} {gtmId && ( <Script id="gtm" strategy="afterInteractive" // GTM initialization code /> )} {/* Other third-party scripts */} </> ); });

Best Practices

  1. Use Next.js Script Component
    1. Utilize the next/script component with strategy="afterInteractive" for optimal loading
    2. Scripts will load after the page becomes interactive
  2. Conditional Loading
    1. Load scripts only after user interaction
    2. Respect user's cookie preferences
    3. Consider geographical location for GDPR compliance
  3. Performance Optimization
    1. Use script loading strategies wisely
    2. Implement timeout fallbacks
    3. Clean up event listeners
  4. GDPR Compliance
    1. Check user's location
    2. Implement cookie consent mechanism
    3. Store user preferences

Implementation Benefits

Performance Metrics

  • Reduced Initial JavaScript Bundle
  • Improved First Input Delay (FID)
  • Better Largest Contentful Paint (LCP)

User Experience

  • Faster initial page load
  • Prioritized core content loading
  • Respectful of user privacy preferences

Conclusion

By implementing delayed script loading with user interaction detection and GDPR compliance, you can significantly improve your Next.js application's performance while maintaining necessary analytics and third-party functionality.

Remember to:

  • Test performance impact
  • Monitor Core Web Vitals
  • Regularly review and update third-party scripts
  • Keep GDPR compliance up to date

This approach ensures a balance between functionality and performance, resulting in a better user experience and improved SEO metrics.

Published: May 25, 2025