Back
joijoijoij
February 26, 20265 miniuhhih

joijoijoij

Hello 👐,

In this article I want to talk about <Script /> tag in Next.js.

Problem:

We decided to migrate our vanilla javascript app to react.js/next.js application. But we didn’t convert all components to react.js style. We just copied our js files to new project and imported with <Script /> tag. At first everything looks fine but when I wraped some elements with <Link /> tag from next.js/link package an error occured.

The problem is <Script /> tag only run once. For example when I navigate to new page with <Link /> tag, <Script /> tag will execute but If I leave this page and come back again <Script /> tag won’t work this time. So I have to refresh page for execute <Script /> tag again.

And I made some research. Found this discussions.

So, for execute script tag for every render just give a unique url.

<Script src={`/path/to/script?v=${Math.random() * 999}`} type='module' />`

I just gave unique version number so Next.js don’t use cached src.

And created a hook for maintain <Script /> tags.

import React, { ReactNode, useState, useEffect } from "react"
import { useRouter } from "next/router"
import Script from "next/script"

const useScript = (paths: string[]) => {
 const [scriptTag, setScriptTag] = useState<ReactNode>(<></>)
    const router = useRouter()


    useEffect(() => {
        if (!router.isReady) return

        router.events.on("routeChangeStart", () => {
            setScriptTag(<></>)
        })

        setScriptTag(<>
            {paths.map((x, i) => {
                return <Script key={i} className='script-tag-js' src={`${x}?v=${Math.random() * 999}`} type='module' />
            })}
        </>)


        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [router.asPath, router.isReady])

    return { scriptTag }

}

export default useScript
  • useScript tag takes paths parameter that represents script sources.

  • After I created react fragment with useState hook. So I can rerender the <Scripts />

  • I added two dependency for useEffect. router.asPath so when the url is changed this useEffect hook will fire again. And when this hook fires I put script tags inside the scriptTag variable. Also I clean the other script tags from previous page. Because we don’t use these scripts in new page.

  • And last but not least. I returned scriptTag (which is ReactNode type) variable.

Use this useScript hook:

I have a <Container /> component.

I wrap each component with container under the page folder.

const Container: FC<ContainerProps> = ({ children, headerType, scripts = [] }) => {

    const { scriptTag } = useScript(['/assets/scripts/helpers/header.js', '/assets/scripts/helpers/modal.js', ...scripts])

    return <>
        <Header />
        {children}
        {scriptTag}
        <Footer />
    </>
}

export default Container
  • This container accepts script but initially it is empty array.

  • And I passed this scripts variable to useScript hook.

  • I have some fixed scripts for every page I put these as a parameter. And spreaded scripts array. Because we shouldn’t have nested arrays.

  • scriptTag is ReactNode that contains <Script /> tags. And I returned this scriptTag variable.

  • So now after every route change new scripts will atached and previous scripts will remove.

Thank you so much for read this article. See you byee.