Edit +++ ON HOLD I just had a lightbulb moment. I will post my discovery/misunderstanding in the next few hours. This ticket can probably be closed then, but I have to test my new theory first…
Multiple (duplicate) meta tags such as charSet
and viewport
are rendered in <head>
section of the HTML output when the <Head>
component is used in both a custom _document
and a page file.
Even relying on the default injection of <meta charSet="utf-8"/>
(i.e. not specifying that tag at all) results in having two of the same tags in the output HTML, one from Document and one from the page.
I also end up with two viewport
meta tags, one from Document and one from the page.
I have created (failing) tests here: https://github.com/Manc/next.js/tree/test-head-document
[email protected]:Manc/next.js.git
test-head-document
yarn install
yarn testonly --testPathPattern "app-document" -t "It dedupes head tags with the same key"
I expect the meta tags with the same key
to override the meta tags from the custom Document (_document
).
I expect only to have one charset
and one viewport
tag in the whole rendered HTML.
canary
branchI'm pretty new to Next.js and don't really know how it all works yet. I hope the test I provided helps somebody more experienced to fix this or maybe give me a hint. Thanks!
My suspicion is that the Head
component of the Document (import { Head } from 'next/document'
) and the Head
component used in the page (import Head from 'next/head'
) might be completely separate and don't "communicate" with each other, each doing their own thing twice.
I had the same problem
OK, here we go… As suspected earlier I finally worked it out.
Apparently, the correct way of using the two different (!) Head
components is to prepare the meta tags etc. only in the page component or other components, but not in the _document.js
file. In the _document
only include it from next/document
like <Head />
without any children.
To clarify…
// pages/_document.tsx
import Document, { Head, Main, NextScript } from 'next/document'; // We import `Head` from `next/document`!
class MyDocument extends Document {
public render() {
return (
<html lang="en">
<Head /> // Do not add any children here!
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
export default MyDocument;
// pages/examplepage.tsx
import React from 'react';
import Head from 'next/head'; // We import `Head` from `next/head `!
import Layout from '../components/layout';
class ExamplePage extends React.Component {
render() {
return (
<Layout>
<Head>
<title>Example page</title>
<meta name="description" content="Example page description" />
</Head>
<h1>Example page</h1>
</Layout>
);
}
}
export default ExamplePage;
And then in the Layout
component you can define default meta tags and headers, but not in the _document
itself.
I was used to the way react-helmet works. There you can set defaults at top level and override/add values in children, but you always use the same component. Here we use two different components with the same name, which, I guess, caused the confusion.
@Manc why have you closed this issue? It's clearly a bug.
_document.js
is the place to specify the defaults for the whole site like charset and viewport.
Now we've got them duplicated with "defaults" later. Even using key
prop like in this test scenario
https://github.com/zeit/next.js/blob/18a9c7e371efc4c487f9c3599c3211ce30009d6c/test/integration/client-navigation/pages/head-duplicate-default-keys.js
doesn't help. In my case, I've got two viewport tags, first one is mine
<meta name="viewport" content="width=device-width, initial-scale=1"/>
defined in _document.js
using next/head
and then the default one later
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"/>
The same with charSet
.
Please, reopen this issue, maybe we'll get an explanation.
@gothy You might be right, it's probably best to get the opinion of the Next.js team, what the intended usage is.
If we are supposed to define defaults in the _document.js
, which makes sense, then it looks like a bug indeed. If one is supposed to prepare all the head tags outside _document.js
(as described in my previous comment), then it should be made clearer and maybe the Head
component of next/head
should not even accept children.
next/head
should be used in _app / pages.
_document is only for the initial document HTML and if you're going to override on the page level it won't work as we simply won't know about it on the client-side as _document is server-side only.
@timneutkens thanks for clarifying!
I had the impression, _document.js
should contain all of the basic\shared\non-dynamic things like charset, viewport, manifest, icons, etc.
If I move the <meta charSet="utf-8" />
to the _app.js
, I'm getting
Error: A charset attribute on a meta element found after the first 1024 bytes.
from validator.w3.org . I'm not sure if that's super bad, but it's not clear how can I avoid this without putting this tag first inside the _document.js
<Head>
since there's a huge list of i18n strings is being embedded in _document.js
. Anything listed in _app.js
is serialized after initial portion during SSR.
I've seen this PR on deprecating next/head
. It would be great if the new take on this static\dynamic head handling will take W3 validation into account.
And thanks for Next 🚀😀
As said _document is for the initial html, so head elements that never change are fine there. In general you don’t need to add a charset as next comes with UTF-8 by default.
Yes. The (W3 validator) issue arises only when you've got something big serialized into the head in _document.js
, since charset meta tag always comes after that.
My experience was, if I remember correctly now, as soon as I start adding defaults in the _document head, I end up with duplicate meta tags.
@gothy I also wanted to follow the recommendation to set charSet as far at the top as possible. This does not seem to be guaranteed if you rely on Next's defaults.
My solution (workaround?) is, as described above, do not set any defaults in the _document head, only in pages etc. For example, I use a Layout
component, used by all pages and I set my default meta tags there and override them in individual pages.
@Manc yeah that's correct, defaults in _document is fine, if you want to override however you need everything available client-side so that we can actually dedupe and recalculate the items.
I'll close the issue now that it's cleared up.
I think this is still a problem. We have to have some things in our document.js <Head>
which will "push down" all other tags in the <head>
. The result is that the charSet meta is too far down the document according to things like W3 validator. There is no solution for this currently. As we have seen, putting the meta
on top of the mandatory scripts in document.js is not a solution.
Adding an example of what @rscharfer is referring to:
_document.js
<Html>
<Head>
<meta charSet="utf-8" /> {/* Trying to add this at the very top of <head> */}
{/* scripts, RSS feed etc. */}
</Head>
</Html>
This will lead to duplicate meta charset tags.
And without adding the tag explicitly, Next will add it after your custom tags, which in this example would be way too far down. Adding the tag in a page component doesn't work either; Next will append it.
Is there a way to prevent the extra addition of the charSet meta tag in the lib/head.js
file, even explicitly (for instance, by using a flag)? In general, its usage is problematic: the tag is inserted way too low in the head, therefore causing site validation to throw errors. This seems to be problematic, even if head is added to the _app.js
file.
I have the same issue. I set my meta viewport in _document.js. Still a second meta viewport is added:
<meta name="viewport" content="width=device-width"/>
I do not set it myself.
A solution I found for this was to leave the _document
as is, but add the viewport
metadata in a next/head
component inside each page. This will cause the "page" viewport
metadata to override the default one set by _document
and leaves the charset
metadata (set by _document
) at the very top.
I ended up creating a custom Head
component (that uses next/head
) which already sets the viewport
metadata (so I don't have to repeat it on every page).
I'm using the SSG of Next.js, so maybe this doesn't apply for SSR pages, don't know. Hope it helps 👍
Also seeing a duplicate meta viewport overriding the one I specify:
A solution I found for this was to leave the
_document
as is, but add theviewport
metadata in anext/head
component inside each page. This will cause the "page"viewport
metadata to override the default one set by_document
and leaves thecharset
metadata (set by_document
) at the very top.I ended up creating a custom
Head
component (that usesnext/head
) which already sets theviewport
metadata (so I don't have to repeat it on every page).I'm using the SSG of Next.js, so maybe this doesn't apply for SSR pages, don't know. Hope it helps +1
Do you also use redux? because, i'd same problem when i declare next/head in pages/index.js it not show meta, but when i placing it in withReduxStore (custom wrapper for redux store like with-redux-store.js in this link : https://github.com/vercel/next.js/issues/8240), it generate double meta tag on detail page.
maybe next/head should not have the default meta, like viewport and charset
as overwrite in every page is not a good idea
I keep seeing Next/Head with meta tags and style sheet refs in both _document and _app files in nextjs examples. Are there any advantages in setting it in _document?
I have the same issue. I set my meta viewport in _document.js. Still a second meta viewport is added:
<meta name="viewport" content="width=device-width"/>
I do not set it myself.
Also getting this. Did you find a fix @switz or @hk86?
Either this should be reopened, or the examples should be cleared up: i.e. the Google Analytics example has script tags in the <Head>
component of the _document
, but doing so puts the aforementioned scripts before the charset
declaration, and as noted in this issue there is with no way to override this without duplication.
So either offer a way to override, or document explicitly that you should avoid putting scripts (or anything actually) in the
of the_document
if you want it to come after the charset
declaration (which should be in the first 1024 bytes).
Of course I'd rather have the override, as it solves even the viewport meta tag problem.
I haven't found a fix. More a workaround. I set
import Head from 'next/head';
<Head>
<meta key="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
</Head>
at _app
. If I set it at _document
import { Head } from 'next/document';
<Head>
<meta key="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
</Head>
I end up with a second viewport:
<meta key="viewport" name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, viewport-fit=cover" />
<meta name="viewport" content="width=device-width"/>
Either this should be reopened, or the examples should be cleared up: i.e. the Google Analytics example has script tags in the
<Head>
component of the_document
, but doing so puts the aforementioned scripts before thecharset
declaration, and as noted in this issue there is with no way to override this without duplication.So either offer a way to override, or document explicitly that you should avoid putting scripts (or anything actually) in the of the
_document
if you want it to come after thecharset
declaration (which should be in the first 1024 bytes).Of course I'd rather have the override, as it solves even the viewport meta tag problem.
You're right and I'm putting head scripts in my Layout component with next/head
I updated on this issue: https://github.com/vercel/next.js/discussions/17020
We have this issue with our TMS. And realized anytime we have a script that does a document.head.append, a meta-tag gets duplicated in the Head. If we just add a script to our Layout, which basically does a document.createElement and then document.head.append, a meta-tag gets duplicated. If I add multiple calls to this, then multiple meta-tags get duplicated. The interesting part, is I am not even adding meta-tags to the Head, I am adding div tags to the head and it just randomly duplicates a meta-tag.
Most helpful comment
I have the same issue. I set my meta viewport in _document.js. Still a second meta viewport is added:
<meta name="viewport" content="width=device-width"/>
I do not set it myself.