next.js: Failed to execute 'createElement' on 'Document' | v9.5.4
Bug report
Describe the bug
I ran into this error after updating Next.js to version 9.5.4. The problem is that you cannot use React Components inside Head
in _app.js file.
Minimal Example:
const Metadata = () => (
<>
<base href="/" />
<meta name="msapplication-TileColor" content="#000000" />
{/*...*/}
</>
)
class NextApp extends App {
render() {
return (
<>
<Head>
<title>Next Bug Report</title>
{/*Error*/}
<Metadata />
</Head>
</>
)
}
}
To Reproduce
Just open the dev server
Reproduction repo: next-bug-report
Screenshots
System information
- Version of Next.js: [9.5.4]
- Version of React/ReactDOM: [16.13.1]
- Version of Node.js: [14.0.0]
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 54
- Comments: 28 (17 by maintainers)
Commits related to this issue
- fix error see: https://github.com/vercel/next.js/issues/17721 — committed to allenai/supp.ai by jonborchardt 4 years ago
- fix issue with next/head component see: vercel/next.js#17721 — committed to hkennyv/kennyvh.com by hkennyv 3 years ago
Also having this issue when trying pass a component into the
<Head>
@jflayhart in our case we just called a function that returns an array of
links
toprefetch
,preconnect
. so:instead of:
That fixed our problem on dev build and removed the error on console in production. Maybe that could help, in some cases.
@jflayhart 100%! I was struggling with this and switching to calling as “functional”
{Thing()}
instead of component<Thing />
along with wrapping JSX return in<React.Fragment>
solved all my issues.I had the same issue as described above with my photography website and was able to make a workaround fix thing for my use case.
The PR I created is here and this contains a diff of the changes.
A description of what I did is as follows:
I had the
next/head
import and<Head />
component in my Layout component, and in there, I imported my Meta component.Because the aforementioned
<Meta />
component contained a React fragment<></>
and a load of html meta tags, this caused the error because they were not directly nested under the<Head />
component.The fix was to bring the
<Head />
component into the<Meta />
component and the html meta tags could be direct children.Fixing the Meta component tests was quite tricky, because the children inside
<Head />
would not render, so I had to mock the<Head />
component usingjest.mock('next/head')
.After all that, I was able to get things working and reach full test coverage once again.
Hope this helps someone else. An interesting challenge to solve!
I was discussing this with @devknoll (who opened #17920 after I talked to him). He made the very relevant point that if we fix the issue + render the head correctly on client-side navigation it’ll actually further break apps that relied on the behavior we had in previous versions. E.g. what if they’re injecting
<script>
or<link>
tags that would suddenly get injected on client-side routing where they previously did not. AlsouseContext
and other React hooks that would be executed wrongly.Even though not 100% ideal probably the best way to go would be to bring back the previous behavior and then introduce warnings at some point.
Upgrading to the last version we notice the same problem in our project, for example with
ResourceHints
returns an array of react element.if there the plan is remove array/react elements support in the head that change I think should be a major version, otherwise it’s a bug, btw only happens us in dev mode
Because
pages/_document
is just a shell for the page and is never rendered client-side so there’s no hydration involved to ensure it is kept up-to-date between page changes. We can’t tell React to render into only a part of the<head>
, only the full<head>
which would throw away anything that was currently there. Hence whynext/head
has custom hydration bit that knows how to render React elements (which is what JSX compiles to)Just created a small app to verify what I’ve been saying:
<title>
as part of the initial HTML response (can be observed in view-source)<title>
is immediately empty<Link>
to it you’ll notice that<title>
is empty when navigating client-side as well, meaning the tags were not rendered.To further confirm this I went and added another tag as
<title>
has a slight special case innext/head
. But the same behavior happens when you have<meta name="description" content="Description here" />
, it’s part of the initial HTML but nowhere else, once the browser hydrates it’s removed. On client-side navigation it’s not rendered.This was tested on
next@9.5.3
. On9.5.4
and later it indeed throws an error as the logic changed a bit to remove the need fornext-head-count
which caused issues when external scripts were injecting tags (scripts, css etc) into the<head>
.Overall we can bring the behavior that is currently there back, @jflayhart did some work for that in #17770. However that is not ideal obviously as you currently don’t get
<title>
etc when navigating client-side / after the initial page load.Update: just checked the array list method (which next-seo uses) too and it seems that this has the same behavior as having a React component in
9.5.3
, in9.5.4
and later it throws an error. That specific case we can definitely solve as it’s just iterating over the array. I guess #17770 already covers that.I’m using the
next-seo
(https://github.com/garmeeh/next-seo) project, and v9.5.4 breaks it entirely due to this issue. Should this plugin “never have worked” essentially? CC @garmeehYeh this is a catch22 because we can’t apply the open redirect security patch due to this major bug. Site doesn’t function based on our use of scripts and other functionality that is required in the
<Head>
and we rely on our metadata as a SEO-driven PWA.Any suggestions on how to work around this issue until it’s fixed? Here’s an example of how we use it:
resulting in functionality issues per the runtime error. It “silently” fails in prod, meaning the page may still render depending on the Head implementation, but it completely kills dev environment per OP.
There’s still a problem that #17920 doesn’t fix: non-title elements rendered by custom components will be orphaned, where previously they would just be deleted when Next initialized.
I’m not sure yet how that should be addressed.
The concern is that both are breaking changes. The
next-head-count
causes a breakage for anyone who was using custom components. Adding client side rendering could then break anyone who was accidentally relying on those to render with context or not render on the client.I think we should fix it but:
Yeh that makes alot of sense now, @timneutkens. Thanks for that example app.
Agreed, and I made changes to my PR based on your valid concerns. I hope my PR gets us to where we all want to be, especially now that I am less ignorant of the docs and more aware of the intentions behind
next/head
.Also what is the purpose of having all the head tags attached to the global NEXT_DATA object? In our case we have a large list of domains we prefetch, preconnect, to improve preformance and this list would increase the initial HTML of the page.