Next.js: Head elements not being rendered correctly

Created on 8 Nov 2018  路  5Comments  路  Source: vercel/next.js

Bug report

When using React.Fragment or anArray as a child of the next/head component, all the tags that are inside of them are not being rendered/parsed correctly into the DOM.

Describe the bug

I have an use case where I need to create a component that wraps head elements.

This component lives in a repository that is agnostic to NextJS, so I cannot encapsulate those head elements with the Head component provided by next/head directly.

Currently I'm returning those elements wrapped by a React.Fragment element, so it looks something like this:

<React.Fragment>
  <title>{props.title}</title>
  <meta name="description" content={props.description} />
</React.Fragment>

And in the repository where I'm using NextJS, I'm using that component like this:

<div>
  <Head> // This comes from next/head
    <ComponentThatRendersHeadTags title="Title" description="description" />
  </Head>
  ...
</div>

However, when I mount that component and go to the head tag in my DOM, I can see that the title tag is being rendered with empty content (looks something like this: <title></title> and the meta tags are being rendered correctly but they don't have the next-head class that should be applied to them (don't know if this can cause problems when using Links or the Router itself).

I'm currently doing a workaround to this where I'm sending the next/head component as a prop to the component rendering the tags, so it looks something like this:

const { WrapperComponent } = props
return (
  <WrapperComponent>
    <title>{props.title}</title>
    <meta name="description" content={props.description} />
  </WrapperComponent>
)

And this approach is working correctly, filling the title tag with content and applying the next-head class to all of them.

P.S.: I actually don't know if this behavior of not accepting React.Fragment in Head is expected 馃槄

Expected behavior

Even though the workaround is working correctly, it would be cool if the next/head component supports React.Fragment or even an Array as a child.

System information

  • OS: macOS
  • Browser (if applies): Chrome
  • Version of Next.js:7.0.2

Additional context

Heys guys, I have been using NextJS for over a year now and I really love it 鉂わ笍
Keep the hard work 馃挭 thanks for everything 馃槃

Most helpful comment

import Head from 'next/head'
export default ({ title, description, keywords, url = "", language }) => (
    <Head>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
        <meta name="theme-color" content="#118b92" />
        <link rel="manifest" href="/static/manifest/manifest.json" />

        <title>{title}</title>
        <meta name="description" content={description} />
        <meta name="keywords" content={keywords} />
        <meta name="author" content="..." />

        <meta property="og:site_name" content="..." />
        <meta property="og:title" content={title} />
        <meta property="og:url" content={`...${language}/${url}`} />
        <meta property="og:image" content={...} />
        <meta property="og:description" content={description} />
        <meta property="og:type" content="website" />

        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
        <meta name="twitter:image" content={...} />
        <meta name="twitter:card" content="summary_large_image" />

        <link rel="alternate" href={`...${url}`} hrefLang="nl" />
        <link rel="alternate" href={`...${url}`} hrefLang="en" />
        <link rel="alternate" href={`...${url}`} hrefLang="fr" />

        <link rel="icon" type="image/png" href={...} sizes="16x16" />
        <link rel="icon" type="image/png" href={...} sizes="32x32" />
        <link rel="apple-touch-icon" href={...} />
        <link rel="apple-touch-icon" sizes="180x180" href={...)} />
        <link rel="mask-icon" href={...)} color="#d04819"/>
        <link rel="shortcut icon" href={...} />
        <meta name="theme-color" content="#118b92" />
    </Head>
);

All 5 comments

I don't think we should change the next/head API, it's been working fine for 99% of use cases, it accepts direct components as children and that should be enough to cover use cases. The reason I don't want to change the API is that I don't think that we should make the next/head module bigger / shipping code that won't be used by most users or when there is a workaround. I hope this makes sense to you.

@timneutkens I understand your point but I'm confused that this is not an issue for more people. Don't other people create a MetaTag component of sorts where they can simply set all the meta tags shared between pages but also customise them based on the current page?

If I don't use the component I need to manually copy this and change 21 properties for each page? 馃槶

MetaTag component

import React, { Fragment } from "react";

export default ({ title, description, keywords, url = "", language }) => (
    <Fragment>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
        <meta name="theme-color" content="#118b92" />
        <link rel="manifest" href="/static/manifest/manifest.json" />

        <title>{title}</title>
        <meta name="description" content={description} />
        <meta name="keywords" content={keywords} />
        <meta name="author" content="..." />

        <meta property="og:site_name" content="..." />
        <meta property="og:title" content={title} />
        <meta property="og:url" content={`...${language}/${url}`} />
        <meta property="og:image" content={...} />
        <meta property="og:description" content={description} />
        <meta property="og:type" content="website" />

        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
        <meta name="twitter:image" content={...} />
        <meta name="twitter:card" content="summary_large_image" />

        <link rel="alternate" href={`...${url}`} hrefLang="nl" />
        <link rel="alternate" href={`...${url}`} hrefLang="en" />
        <link rel="alternate" href={`...${url}`} hrefLang="fr" />

        <link rel="icon" type="image/png" href={...} sizes="16x16" />
        <link rel="icon" type="image/png" href={...} sizes="32x32" />
        <link rel="apple-touch-icon" href={...} />
        <link rel="apple-touch-icon" sizes="180x180" href={...)} />
        <link rel="mask-icon" href={...)} color="#d04819"/>
        <link rel="shortcut icon" href={...} />
        <meta name="theme-color" content="#118b92" />
    </Fragment>
);

You can have multiple usages of next/head actually, or dependency inject @AlexisGlez was doing already

You can have multiple usages of next/head actually, or dependency inject @AlexisGlez was doing already

I'm not sure what you mean with your answer, do you have an example where I can make sure that I don't need to manually copy the tags listed in my previous reaction?

import Head from 'next/head'
export default ({ title, description, keywords, url = "", language }) => (
    <Head>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
        <meta name="theme-color" content="#118b92" />
        <link rel="manifest" href="/static/manifest/manifest.json" />

        <title>{title}</title>
        <meta name="description" content={description} />
        <meta name="keywords" content={keywords} />
        <meta name="author" content="..." />

        <meta property="og:site_name" content="..." />
        <meta property="og:title" content={title} />
        <meta property="og:url" content={`...${language}/${url}`} />
        <meta property="og:image" content={...} />
        <meta property="og:description" content={description} />
        <meta property="og:type" content="website" />

        <meta name="twitter:title" content={title} />
        <meta name="twitter:description" content={description} />
        <meta name="twitter:image" content={...} />
        <meta name="twitter:card" content="summary_large_image" />

        <link rel="alternate" href={`...${url}`} hrefLang="nl" />
        <link rel="alternate" href={`...${url}`} hrefLang="en" />
        <link rel="alternate" href={`...${url}`} hrefLang="fr" />

        <link rel="icon" type="image/png" href={...} sizes="16x16" />
        <link rel="icon" type="image/png" href={...} sizes="32x32" />
        <link rel="apple-touch-icon" href={...} />
        <link rel="apple-touch-icon" sizes="180x180" href={...)} />
        <link rel="mask-icon" href={...)} color="#d04819"/>
        <link rel="shortcut icon" href={...} />
        <meta name="theme-color" content="#118b92" />
    </Head>
);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

formula349 picture formula349  路  3Comments

lixiaoyan picture lixiaoyan  路  3Comments

jesselee34 picture jesselee34  路  3Comments

flybayer picture flybayer  路  3Comments

kenji4569 picture kenji4569  路  3Comments