Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unmount next/script on page change in next.js?

I want to load a script only on certain pages. Next.js recommends using next/script tag for it. However, when I navigate to some different pages I can still see the script present at the end of body in HTML.

enter image description here

import Script from "next/script";

const Comments = () => {
  return (
    <div className="giscus mt-16">
      <Script
        src="https://giscus.app/client.js"
        data-repo="GorvGoyl/Personal-Site-Gourav.io"
        data-repo-id="MDEwOlJlcG9zaXRvcnkyOTAyNjQ4MTU="
        data-category="Announcements"
        data-category-id="DIC_kwDOEU0W784CAvcn"
        data-mapping="pathname"
        data-reactions-enabled="0"
        data-emit-metadata="0"
        data-theme="light"
        data-lang="en"
        crossOrigin="anonymous"
        strategy="lazyOnload"
        onError={(e) => {
          console.error("giscus script failed to load", e);
        }}
      ></Script>
    </div>
  );
};

I suspect Next.js is not cleaning up the script on route change action. How do I make sure that scripts get removed on page change?

like image 982
GorvGoyl Avatar asked Jan 30 '26 11:01

GorvGoyl


1 Answers

I was facing the same problem. Only solution that works for me is to reload the page as the routeChangeStart. I also made it in a custom Hook so can be reused with ease.

Custom Hook

import { useRouter } from "next/router";
import { useEffect } from "react";

export default function useScript(url: string) {
    const router = useRouter();
    useEffect(() => {
        const script = document.createElement("script");
        script.src = url;
        script.async = true;
        document.body.appendChild(script);

        // Needed for cleaning residue left by the external script that can only be removed by reloading the page
        const onRouterChange = (newPath: string) => {
            window.location.href = router.basePath + newPath;
        };
        router.events.on("routeChangeStart", onRouterChange);

        return () => {
            router.events.off("routeChangeStart", onRouterChange);

            document.body.removeChild(script);
        };
    }, [router, url]);
}

Explaination:

  • This hook creates new script element, sets its url to provided url and appends it to body
  • Importing script is easy the hard part is to remove it and all the residue it leaves behind mailny on window object, the hacky and easy way is to just reload the page
  • So we are adding event listener for route change and then we are redirecting to that page so the whole page refreshes and all the previous js script get unloaded

Usage:

Just need to call useScript with url of javascript

import React from "react";
import NavBar from "../src/components/NavBar";
import useScript from "../src/hooks/useScript";

type Props = {};

const ScriptLoader = (props: Props) => {
    useScript("/js/theScript.js");
    return (
        <>
            <NavBar />

            <div className="container">This is normal Page</div>
        </>
    );
};

export default ScriptLoader;

Note:

If you want to access property set on window object from the script its best to add setTimeout to check if that object is present or not as the loading of script might take some time, after that you can set some flag to true

like image 185
Chaitanya Kulkarni Avatar answered Feb 01 '26 02:02

Chaitanya Kulkarni



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!