When rendering content to the <Head>
component, I found out the order of the Head content is actually reverse than what I expected.
I'd assume when declaring a <Head>
inside the _document.js
file, this content would show before content declared inside components further in the page lifecycle.
Right now it seems <Head>
content is being shifted. So tags declared later are being rendered first in the head.
This can cause issues when trying to define resources to preload if scripts like GA or GTM are being executed first by the browser.
| Tech | Version |
|---------|---------|
| next | ^4.1.4 |
| node | 6 and 8 |
| OS | Ubuntu |
| browser | chrome |
We'd need an example of your _Document JS file to debug this issue.
Here:
// @flow
import * as React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
import cdn from '../modules/cdn';
export default class MyDocument extends Document {
static getInitialProps({ renderPage }: *) {
const sheet = new ServerStyleSheet();
const page = renderPage(App => props => sheet.collectStyles(<App {...props} />));
const styleTags = sheet.getStyleElement();
return { ...page, styleTags };
}
render() {
return (
<html>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script
dangerouslySetInnerHTML={{
__html: `
window.CDN_URL = "${process.env.CDN_URL || ''}";
`,
}}
/>
<link
rel="apple-touch-icon-precomposed"
href={cdn('/next_static/favicon-60.png')}
/>
<link
rel="icon"
type="image/png"
href={cdn('/next_static/favicon.png')}
/>
{this.props.styleTags}
</Head>
<body>
<div className="root">
<Main />
</div>
<NextScript />
</body>
</html>
);
}
}
Hi @SBoudrias, I'm testing your code inside the example basic-css with some little changes to see if I can replicate the error but it's showing the right order for me.
Here's the code I used to test _document.js
import * as React from 'react'
import Document, { Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet()
const page = renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
)
const styleTags = sheet.getStyleElement()
return { ...page, styleTags }
}
render() {
return (
<html>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Testing head order"/>
<meta name="author" content="John Doe"/>
<script
dangerouslySetInnerHTML={{
__html: `
window.CDN_URL = "${process.env.CDN_URL || 'HELLO'}";
`
}}
/>
{this.props.styleTags}
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</Head>
<body>
<div className="root">
<Main />
</div>
<NextScript />
</body>
</html>
)
}
}
And here the html rendered in localhost:
it looks good to me so maybe it's related to a different version of a package/node or to the code I omitted from _document.js
Versions of Next and React are the same as in the example, on Node 9.5.0 and windows 10
Same trouble. I can give you my exmples of code. And as I can see, you, @lfades, replicate it. Because in expected behaviour we are waiting what all head tag will apear just right after first meta tag with charset="utf-8"
and before other scripts and tags.
for example my wrapper decorator for components:
export function withWrapper(Child) {
class Wrapper extends Component {
static async getInitialProps(...args){
if (typeof Child.getInitialProps !== 'function') {
return {}
}
let initialProps = Child.getInitialProps(...args)
if (initialProps instanceof Promise) {
initialProps = await initialProps
}
return initialProps
}
render() {
return <div className={css.main}>
<Head>
<link rel="stylesheet" href="/_next/static/style.css"/>
</Head>
<Header/>
<div className={css.container}>
<Child {...this.props}/>
</div>
</div>
}
}
return Wrapper
}
and my _document.js
import Document, {Head, Main, NextScript} from 'next/document'
export default class MyDocument extends Document {
static getInitialProps({renderPage}) {
const page = renderPage()
return {...page}
}
render() {
return (
<html lang="en" dir="ltr">
{/*don't insert <head></head> otherwise nextjs will render head tag 2 times*/}
{/*and don't insert anything in Head tag, bcs nextjs will insert it in the end of head tag*/}
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="/static/base.css"/>
<link rel="icon" type="image/png" href="/static/favicon.png"/>
</Head>
<body>
<Main/>
<NextScript />
</body>
</html>
)
}
}
Expected Behavior: head tags from _document.js
will be apear before head tags of wrapper.
But:
thats why I place that strange comment in the code ("dont insert code here")
So yeah. It is bug and example where we placing head tags in '_document.js' isn't good exmaples.
@JerryCauser confirmed, it's a bug, I'll see how to fix it
Same issue with a dead simpler example:
_document.js:
import Document, { Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render () {
return (
<html>
<Head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
my-page.js
import React, { Component } from 'react'
export default class MyPage extends Component {
render() {
return (
<div>
<h1 className="msg">Yo</h1>
<style jsx>{`
body {
font-family: arial;
}
.msg {
color: red;
}
`}</style>
</div>
)
}
}
The resulted head puts my component style before bootstrap so is really bad for customizing
@hugotox The issue already has a fix but is not merged
Thank you! You guys are awesome
Is the fix for this issue already merged?
no 馃槩and I'm still getting that awful FOUC with latests version of Next
This issue reminded me of this thread: https://spectrum.chat/?t=0d91eec7-6a38-4943-98f6-0bb3a71d50f6
With the latest next, people seem to need to change the order of their head content. Would be nice to get that fixed if everyone can agree on the right order of things.
@timneutkens What is the status of this issue? Seems like an easy fix.
just do the following in https://github.com/zeit/next.js/blob/canary/packages/next/pages/_document.js#L127
return <head {...this.props}>
{head}
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} as='script' nonce={this.props.nonce} />}
{children}
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} as='script' nonce={this.props.nonce} />
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} as='script' nonce={this.props.nonce} />
{this.getPreloadDynamicChunks()}
{this.getPreloadMainLinks()}
{this.getCssLinks()}
{styles || null}
</head>
Or is there a reason why {children} is appended at the end?
Please merge this 馃檹
@lfades do you think that #5696 can be a remedy for the problem in Next.js 7?
@kachkaev The code has changed a lot since this pr has made, the issue was that the head
content of the children was loaded at the start, if {children}
includes it then yes.
next/static/styles need to be the last one..
This issue is the closest to what we're having with our static export.
The getCssLinks
function of Next's Head
component renders CSS link tags in the order they happen to be in an array.
getCssLinks () {
const { assetPrefix, files } = this.context._documentProps
if(!files || files.length === 0) {
return null
}
return files.map((file) => {
// Only render .css files here
if(!/\.css$/.exec(file)) {
return null
}
For some reasons, while doing a static export of our project, the CSS files are listed as follows:
[
// Component specific styling
'static/css/commons.4e0b01bb.chunk.css',
// Global styling, like our custom bootstrap build
'static/css/styles.685164fb.chunk.css'
]
Our custom build (global styling) is imported in the _app.jsx
file.
// ~ is an alias to our assets directory
import '~/scss/main.scss';
It was working correctly in Next v6, but now, our global styling overrides our specific styling because of this new Head ordering.
Maybe I should open a new issue related to the static export?
Still having the same issue.
This is the only fix that works for now - https://spectrum.chat/next-js/general/control-the-placement-of-styles-chunk-css-in-nextjs7~0d91eec7-6a38-4943-98f6-0bb3a71d50f6
This issue reminded me of this thread: https://spectrum.chat/?t=0d91eec7-6a38-4943-98f6-0bb3a71d50f6
With the latest next, people seem to need to change the order of their head content. Would be nice to get that fixed if everyone can agree on the right order of things.
Since we only have 2 CSS files that are not in the right order, we ended writing a small sub-class of Next's Head
:
export default class Head extends NextHead {
getCssLinks() {
return super.getCssLinks().reverse();
}
}
Just a heads up that when using the static export of Next.js, my small patch above worked in some places, but breaks other pages, so we're rolling that back while evaluating the possibility of ditching the static export in favor of SSR.
@emileber just FYI there is no difference between static export and ssr in terms of rendering.
@timneutkens There is in terms of CSS compilation it seems.
It's literally using the same method to render renderToHTML()
so I'm pretty sure there's something else going on.
It's not the HTML that's not rendered properly, it's the compiled CSS that is divided in different chunks in the static export that are in a different order than while using SSR in development mode.
While using SSR, we're only seeing one CSS bundle in the expected order. Then, after building a static export, there are two CSS bundles and the order of some selectors is now wrong.
Maybe the SSR mode in production will have the same problem in our project?! I'll check to be sure.
I feel like my problem is different from this issue, we thought at first that it was the order in which the linked CSS were rendered in the layout, but it seems more of a CSS plugin configuration problem (though we're not using/overriding anything special).
Sorry for the noise. I'll probably open an issue if I can isolate the problem correctly.
Most helpful comment
Same issue with a dead simpler example:
_document.js:
my-page.js
The resulted head puts my component style before bootstrap so is really bad for customizing