next/head is removing scripts added into head. This issue is currently causing the removal of scripts injected by our third-party tag manager.
https://github.com/bmathews/nextjs-head-issue
Include a script in <Head /> that adds another script synchronously.
import Head from 'next/head';
const dynamicScript = `
var s = document.createElement('script');
s.innerHTML = "console.log('_index log')";
document.head.appendChild(s);
`;
const Index = () => {
return (
<>
<Head>
<title>Index title</title>
<script dangerouslySetInnerHTML={{ __html: dynamicScript }} />
<meta name="theme-color" content="#ff6868"/>
</Head>
<div>Hello world</div>
</>
);
}
export default Index;
next/head doesn't remove dynamically added scripts
I added "break on subtree modifications" to head. During hydration, before next/head performed removal, you can see the script console.log('_index log')

During removal, oldTags value in head-manager.js is containing that script:

After removal, console.log('_index log') is no longer there.

I'd like to bump this issue as it's causing a problem for us too. We have a 3rd party chat bot on our application that injects a script into the head. On initial load, the script is getting wiped since we are using
in a base component. If the user refreshes the page, the chat bot then works since I assume the cached server-rendered page gets pulled and the client-side component then can successfully inject into the head without getting wiped.@mitchell-bu Don't know what the difference is tbh, but we got around the issue by moving our 3rd party script to _document.js's head rather than from a lower level component.
Yea, not sure that's really going to be an option for us.
Interestingly, discovered that this was introduced in the 9.0.3 version. 9.0.2 doesn't have this problem.
I'm fairly certain this is the PR that introduced this problem, which was opened by @devknoll : https://github.com/zeit/next.js/pull/8020/files
Specifically the change to head-manager.js.
I frankly don't entirely follow what this is trying to do so I'm not really able to suggest a fix. We ultimately found a workaround using dynamic imports to delay importing the component that injects into the head until the client renders.
Document is not parsed the same way. Document is SSR only, passing script tags through React does not work because the react parser does not accept scripts. The head manager does seem to have a cleanup step, but it does not seem to write scripts back to the page beyond what is flushed out of webpack initially.
this problem was introduced by https://github.com/vercel/next.js/commit/e68307df3a1e4f1cc6da7b435517984f91aed8b3#diff-6d15c59f75bd4f8cdcbd29588c610545 to fix the SEO problems in https://github.com/vercel/next.js/issues/3494. that makes sense, however, the new implementation is tenuous. I think it's better to go back to a system where you have an explicit handle on which elements were added by Next. instead of classes, you could use data attributes. i have never seen an SEO problem with using those to manage HEAD tag reconciliation.
is it possible to offer reconciliation based on data attributes as an experimental feature? my team would be glad to try it out. i think it would resolve our issues and we would be able to validate that SEO is unaffected by data attributes.
@wawjr3d solution sounds great. And based on documentation on data-attributes (https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes), the SEO crawler will not index values from data-attributes.
have this issue too! commenting for exposure. was trying to add a script tag for tracking and was trying to figure out what was going on
Have a similar issue too, but for styles which are injected by another script of ours. Imagine could be the same root cause so wanted to add a comment here as it seems like not just <script> elements are affected.
Most helpful comment
@mitchell-bu Don't know what the difference is tbh, but we got around the issue by moving our 3rd party script to
_document.js'sheadrather than from a lower level component.