Home > Software engineering >  NextJS script tag
NextJS script tag

Time:10-25

So I've been trying at this for quite some time now.

On this site I need to be able to place a script tag that will embed a contact form and newsletter sign up form etc to specific pages. So for example on the contact page the contact form script tag will live.

I've tried a bunch of different methods but I'm unable to get the result I'm after for what seems like a pretty basic thing to be able to do.

What I'm finding is initially it seems like it works fine, the script tag works and I see what I expect on the page. But then when you navigate away from the page and go back to it the script tag isn't displaying. It's only when I then refresh the page again will the stuff the script tag is supposed to output appear.

I'm assuming this is happening because with use of the Link tag the website doesn't reload between navigation, instead it all works like a SPA with no reload but it's that refresh that triggers the Script tag to execute.

In my research on this I've seen people recommend not using the Link tag and instead just using a tags so when navigating to the page it has to reload and force the Script tag to display. Only in my case it's not really an option because I am unable to tell which page the client has decided to output a script tag or not because the website is fully dynamic and able to be constructed all via the CMS.

I've tried:

Doing the dangerouslySetInnerHTML trick with the script. Unfortunately the same thing happens as I've described above

<div
  dangerouslySetInnerHTML={{__html: `<script src=""></script>`}}
/>

I've also tried using next's built in Script tag https://nextjs.org/docs/api-reference/next/script with all the different strategy props. Unfortunately the script content either didn't display at all or it acted the same as above.

I've also tried creating a helper function that uses the useEffect to instate the script tag only after navigating to the page, like so:

import { useEffect } from "react"


const useScript = (url) => {

  useEffect(() => {
    const script = document.createElement("script")

    script.src = url
    script.async = true

    document.body.appendChild(script)

    return () => {
      document.body.removeChild(script)
    }
  }, [url])
}

export default useScript;

and then in the component output to a page:

<div>
    {useScript("...")}
</div>

This did work! Every time you navigate to the page with the script on it did output the contents of the script tag - not needing a page refresh. The only thing is the contents of the script tag was always output right at the bottom of the page, below the footer and everything. As opposed to where the actual useScript is put in the component.

I tried altering how the useEffect works by making it append the script into an already existing div in the component:

import { useEffect } from "react"


const useScript = (url) => {

  useEffect(() => {
    const script = document.createElement("script")

    script.src = url
    script.async = true
    script.defer = true

    document.getElementById('scriptEx').appendChild(script)

    return () => {
      document.body.removeChild(script)
    }
  }, [url])
}

export default useScript;

But for some reason this doesn't act the same way as the previous setup. On navigation to the page the contents of the script tag don't display again.

The last method I found that does work every single time, but seems like a really ridiculous method to use is putting the script tag inside of an iFrame:

<iframe
    srcDoc={`
    <!doctype html>
    <html>
        <head>
        </head>
        <body>
            <div>
                <script src="..."></script>
    
            </div>
        </body>
    </html>
    `}
/>

There's got to be a way to do this seemingly simple thing.

If anyone could help it'd be really appreciated!

CodePudding user response:

I think you should remove the child from the element that has the id scriptEx as you removed the script from the body. I think that all you have to do is to put

    return () => {
      document.getElementById('scriptEx').removeChild(script)
    }

instead of

    return () => {
      document.body.removeChild(script)
    }

CodePudding user response:

if you are using NextJs, the solution is very simple. Since v11.0, Next team introduced the Script component from the library next/script, t's similar to the Head component from the library next/head. Basically what you need to do is:

import Script from 'next/script'
export default function Home(){
   return(
      <>
         <Script src="#" async />
      </>
   )
}

You can read more on the Script component on the Next docs here: https://nextjs.org/docs/basic-features/script

  • Related