Next.js: Next/Head - return null for conditional head scripts

Created on 2 Jun 2020  路  4Comments  路  Source: vercel/next.js

Feature request

When using next/head in a page we should be able to render head scripts conditionally by returning null from a custom component.

Is your feature request related to a problem? Please describe.

If a custom component that can return null (depending on its internal logic) is added as a child of Next/Head the page breaks.

Example:

<Head>
  <CustomComponent booleanProp={true} />
</Head>

Where CustomComponent is something like:

const CustomComponent = ({booleanProp}) => {

if(booleanProp){
  return <script>some js script</script>
}

return null;
};

The last 'return null' will break the page since a file called headManager.js has this logic:

  for (var i = 0, j = headCountEl.previousElementSibling; i < headCount; i++, j = j.previousElementSibling) {
    if (j.tagName.toLowerCase() === type) {
      oldTags.push(j);
    }
  }

Therefore j.tagName is undefined.

Describe the solution you'd like

I would like to be able to return null (as it's very common in react components) when my condition is not met; this will let NextJs skip the script.

Something simple like:

    if (j.tagName && j.tagName.toLowerCase() === type) {
      oldTags.push(j);
    }

As an added bonus we can log a warning for head scripts returning null.

Describe alternatives you've considered

headManager.js should handle 'null' script tags.

bug needs investigation

Most helpful comment

@lfades I've just gave it a try and unfortunately it still is an issue (although a different one?):

Uncaught (in promise) DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('function Analytics(_ref) {
...my component...
}') is not a valid name.
    at reactElementToDOM (webpack-internal:///./node_modules/next/dist/client/head-manager.js:20:21)
    at eval (webpack-internal:///./node_modules/next/dist/client/head-manager.js:59:18)
    at Array.forEach (<anonymous>)
    at updateElements (webpack-internal:///./node_modules/next/dist/client/head-manager.js:46:14)
    at eval (webpack-internal:///./node_modules/next/dist/client/head-manager.js:107:9)
  11 | function reactElementToDOM({ type, props }: JSX.Element): HTMLElement {
> 12 |   const el = document.createElement(type)
     |             ^
  13 |   for (const p in props) {
  14 |     if (!props.hasOwnProperty(p)) continue
  15 |     if (p === 'children' || p === 'dangerouslySetInnerHTML') continue

and here's my <Head> component:

      <Head>
        <meta name="web" content={serverName} />
        <Analytics
          {...props}
        />
      </Head>

So now I can't even use a custom component within <Head>, regardless of what it returns (whether a valid DOM element child or a custom React component).
I'm on "next": "^9.5.4-canary.20",

All 4 comments

Likely to be solved in 9.5.4 by https://github.com/vercel/next.js/pull/16758?

@klapec Did you check if this is still an issue on the latest canary?

@lfades I've just gave it a try and unfortunately it still is an issue (although a different one?):

Uncaught (in promise) DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('function Analytics(_ref) {
...my component...
}') is not a valid name.
    at reactElementToDOM (webpack-internal:///./node_modules/next/dist/client/head-manager.js:20:21)
    at eval (webpack-internal:///./node_modules/next/dist/client/head-manager.js:59:18)
    at Array.forEach (<anonymous>)
    at updateElements (webpack-internal:///./node_modules/next/dist/client/head-manager.js:46:14)
    at eval (webpack-internal:///./node_modules/next/dist/client/head-manager.js:107:9)
  11 | function reactElementToDOM({ type, props }: JSX.Element): HTMLElement {
> 12 |   const el = document.createElement(type)
     |             ^
  13 |   for (const p in props) {
  14 |     if (!props.hasOwnProperty(p)) continue
  15 |     if (p === 'children' || p === 'dangerouslySetInnerHTML') continue

and here's my <Head> component:

      <Head>
        <meta name="web" content={serverName} />
        <Analytics
          {...props}
        />
      </Head>

So now I can't even use a custom component within <Head>, regardless of what it returns (whether a valid DOM element child or a custom React component).
I'm on "next": "^9.5.4-canary.20",

I am getting the same issue as @klapec in 10.0.0

Was this page helpful?
0 / 5 - 0 ratings