Skip to content

Handling Script Tags on Next.js Route Changes

  • #Tips & Tricks
  • #Features
Read time: 11 minutes
traffic officer wearing a yellow jacket with the text "<script>" aggressively pointing in different directions and breakdancing in the middle of a busy intersection in Shibuya Tokyo, 4k Hasselblad

When building websites with Next.js and Sanity, you might encounter a common challenge: managing custom HTML that includes <script> tags. By default, these scripts only execute on the initial page load, but what if you need them to run again after route changes? Let's explore how this is effectively handled in SanityPress with the help of the Custom HTML module.

The ChallengeπŸ”—

In Next.js applications, client-side navigation doesn't trigger a full page reload. While this provides a smooth user experience, it can cause issues with custom scripts that need to re-execute after route changes. This is particularly important when working with embedded content or third-party widgets.

The SolutionπŸ”—

We've implemented a custom component in SanityPressβ€”the Custom HTML moduleβ€” that handles this scenario elegantly. Here's how it works:

  1. We use a combination of useRef and useState to track the component's render state.
  2. The component maintains a firstRender flag to handle initial mounting.
  3. On subsequent renders, we:
    1. Create a new DOM fragment from the HTML string
    2. Append it to our container element

Here's the implementation:

'use client'

import { useEffect, useRef, useState } from 'react'

export default function CustomHTML({ code }: { code?: string }) {
  const ref = useRef<HTMLElement>(null)
  const [firstRender, setFirstRender] = useState(true)

  if (!code) return null

  // if no <script> tag, render as is
  
  if (!code.includes('<script'))
    return (
      <section dangerouslySetInnerHTML={{ __html: code }} />
    )

  // if includes <script> tag, ensure script is re-run on each render

  useEffect(() => {
    if (firstRender) {
      setFirstRender(false)
    } else {
      const parsed = document
        .createRange()
        .createContextualFragment(code)

      ref.current?.appendChild(parsed)
    }
  }, [ref.current, code])

  return (
    <section ref={ref} />
  )
}

Best PracticesπŸ”—

While this solution works well, consider these best practices:

  1. Only use custom scripts when absolutely necessary.
  2. Prefer Next.js's built-in next/script component when possible.
  3. Be cautious with third-party scripts and their impact on performance and security.
  4. Always sanitize HTML content before rendering.

Example Use Case in SanityPressπŸ”—

Imagine you have a content block in your CMS where users can embed custom HTML. You don’t want scripts within this block to execute multiple times on each page transition. By implementing the Custom HTML module, you ensure that scripts run cleanly and predictably, improving performance and avoiding errors.

ConclusionπŸ”—

Managing script execution in Next.js doesn't have to be complicated. With this solution implemented in SanityPress, you can confidently handle custom HTML content that includes scripts, ensuring they work correctly throughout your application's lifecycle.

heavily modded white JDM Nissan R33 Skyline with black heart patterns and underbody lights, with the logo "SanityPress", in the streets of Daikoku parking lot at night, Nikon film