javascriptreactjstypescriptnext.jsnext-link

NextJs <Link> doesn't rerun scripts


When I use the <Link> tag in NextJs to navigate between pages it doesn't rerun my scripts after changing pages. It only runs the scripts after the first page load or when I press reload. When using plain <a> tags instead, it works fine because the page reloads after each navigation. As far as I can tell this happens because the <Link> tag makes it a Single-Page Application and doesn't refresh the page when navigating between pages.

I would greatly appreciate anyway to have it rerun the scripts when navigating between pages or to have it refresh the page without using just plain <a> tags and losing the Single-Page Application functionality.

This code doesn't refresh the page

 <Link href="/page1">
   <a>Page 1</a>
 </Link>
 <Link href="/page2">
   <a>Page 2 </a>
 </Link>

This code does refresh the page

 <a href="/page1">Page 1</a>
 <a href="/page2">Page 2 </a>

I load in all my scripts using a scripts component

export default const MyScripts = () => {
    
    return (
        <Script
            strategy="afterInteractive"
            type="module"
            src="/scripts/myScript.js"
            onReady={() => {
                console.log("Ready")
            }}
        /> 
    )
}

One thing I've noticed is that the above onReady function fires each time I change pages. Maybe someone knows how to run the myScript.js from the onReady.


Solution

  • Caution: The hack of using history.pushState will not work reliably, especially when DOM elements need to be fetched using document.querySelector*. Anyway, it's also better to avoid hacks if you can.

    The recommended way for Next.js is to use the Script component with the inline script.

    Simply move the Javascript code from your script tag into a Script component. Remove the HTML script tag and any script file as you no longer need them.

    import Script from 'next/script';
    
    // Inside your exported component:
    <Script id="myScript">
      // Paste the contents of /scripts/myScript.js here (and remove that file)
    </Script>
    

    Another clean way to do this is to use the useEffect React hook. In class-based components, you can use componentDidMount.

    This idea was proposed here: samplep182's comment in: Normal scripts "" not working after page navigation · Issue #4477 · vercel/next.js

    Simply move the Javascript code from your script tag into a useEffect block. Remove the script tag and any script file as you no longer need them.

    import { useEffect } from 'react';
    
    // Inside your exported component:
    useEffect(() => {
      // Paste the contents of /scripts/myScript.js here (and remove that file)
    }, []);
    

    Notes:

    1. It's recommended to have an empty array [] as the dependency list, to execute this on page load only.

    2. For external scripts, please use the Script component with the src attribute.