Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development server has a flash of unstyled content (FOUC) #13058

Closed
nicholaschiang opened this issue May 18, 2020 · 34 comments · Fixed by #14448
Closed

Development server has a flash of unstyled content (FOUC) #13058

nicholaschiang opened this issue May 18, 2020 · 34 comments · Fixed by #14448
Assignees
Milestone

Comments

@nicholaschiang
Copy link
Contributor

Bug report

Describe the bug

A clear and concise description of what the bug is.

When using Next.js, it appears that the CSS isn't fully hydrated into the <head> when the <div id="__next"> element first becomes visible.

This causes a flash of unstyled content (or FOUC) when running our development server. It seems like it's fine in production though (which seems strange to me).

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Clone this repository by running:
$ git clone https://github.com/tutorbookapp/covid-tutoring
  1. Install our dependencies (more details in our README.md) by running:
$ npm i && lerna bootstrap --hoist
  1. Start a development server by running:
$ npm run dev
  1. Notice the FOUC when first loading the page.

Expected behavior

A clear and concise description of what you expected to happen.

The <div id="__next"> element should only be visible after the necessary above-the-fold stylesheets (i.e. the style sheets that are included in the React components that are visible above-the-fold) are inserted into <head>. The remaining stylesheets can then be loaded with the <div id="__next"> element visible.

Screenshots

If applicable, add screenshots to help explain your problem.

See the FOUC visible on our development server:

fouc

Notice that it's gone on our production website:

no-fouc

System information

  • OS: Ubuntu 18.04.2
  • Browser: Firefox 76.0.1
  • Version of Next.js: 9.4.0
  • Version of Node.js: 12.16.1
@nicholaschiang nicholaschiang changed the title Automatic optimization causes flash of unstyled content (FOUC) Development server has a flash of unstyled content (FOUC) May 18, 2020
@Timer Timer added this to the 9.4.2 milestone May 18, 2020
@ghost
Copy link

ghost commented May 18, 2020

I am also facing this problem when I use css modules but when I use styled-jsx then It works fine.

@Timer Timer modified the milestones: 9.4.2, 9.4.3 May 20, 2020
@robgraeber
Copy link

I notice a similar issue that global css in _app.js doesn't seem to be loaded with javascript off while it's in dev mode. It makes SSR harder to test since there could be missing styles.

@derskeal
Copy link

@robgraeber I have the exact issue you do. The CSS is being compiled into _app.js instead of a separate css file.

@dnaranjo89
Copy link

Same issue here.
I've created a minimum example reproducing this issue: https://github.com/dnaranjo89/next-css-ssr

@robgraeber
Copy link

robgraeber commented May 22, 2020

Also sometimes I'll edit some global css in inspector and any change causes the whole page's css to be wrecked somehow. Has anyone experienced that?

@robgraeber
Copy link

@derskeal a workaround is to use this sass plugin and import your stylesheets via sass in your Layout component: https://github.com/giuseppeg/styled-jsx-plugin-sass

@yanv1991
Copy link

yanv1991 commented Jun 1, 2020

@Timer I could reproduce this issue in this repo https://github.com/yanv1991/demo-react-dom, this is not loading the styles using built in sass modules with a dynamic loaded component for ssr in prod mode

@Timer
Copy link
Member

Timer commented Jun 22, 2020

This project cannot be ran:

error - Error [FirebaseError]: projectId must be a string in FirebaseApp.options
    at new FirestoreError (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/firestore/dist/index.node.cjs.js:1223:28)
    at Function.Firestore.databaseIdFromApp (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/firestore/dist/index.node.cjs.js:21018:19)
    at new Firestore (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/firestore/dist/index.node.cjs.js:20850:42)
    at /Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/firestore/dist/index.node.cjs.js:22871:66
    at Component.instanceFactory (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/firestore/dist/index.node.cjs.js:22410:16)
    at Provider.getOrInitializeService (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/component/dist/index.cjs.js:219:39)
    at Provider.getImmediate (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/component/dist/index.cjs.js:120:33)
    at FirebaseAppImpl._getService (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/app/dist/index.node.cjs.js:228:49)
    at FirebaseAppImpl.firebaseAppImpl.<computed> [as firestore] (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/app/dist/index.node.cjs.js:440:39)
    at Object.serviceNamespace [as firestore] (/Users/joe/Desktop/scratch2/covid-tutoring/node_modules/@firebase/app/dist/index.node.cjs.js:420:45)
    at eval (webpack-internal:///./src/firebase/db.tsx:19:124)
    at Module../src/firebase/db.tsx (/Users/joe/Desktop/scratch2/covid-tutoring/.next/server/static/development/pages/_app.js:128:1)
    at __webpack_require__ (/Users/joe/Desktop/scratch2/covid-tutoring/.next/server/static/development/pages/_app.js:23:31)
    at eval (webpack-internal:///./src/firebase/user.tsx:12:61)
    at Module../src/firebase/user.tsx (/Users/joe/Desktop/scratch2/covid-tutoring/.next/server/static/development/pages/_app.js:152:1)
    at __webpack_require__ (/Users/joe/Desktop/scratch2/covid-tutoring/.next/server/static/development/pages/_app.js:23:31) {
  code: 'invalid-argument',
  toString: [Function]
}

@Timer Timer self-assigned this Jun 22, 2020
@kodiakhq kodiakhq bot closed this as completed in #14448 Jun 22, 2020
kodiakhq bot pushed a commit that referenced this issue Jun 22, 2020
We previously used to remove our FOUC helper inside of the style injection to ensure content was shown as fast as possible.

This behavior, however, was problematic for a few reasons:

1. Large JavaScript chunks would take longer than an animation frame to parse, causing FOUC
1. Rendering would sometimes complete before an animation frame, causing improper effects

To fix the latter, we started removing the no FOUC helper **before** rendering, however, we never fixed the former by removing the dead code.

There's not a great way to test this because the FOUC is so fast and flaky, however, this code really shouldn't exist and isn't likely to be re-added (regress).

Also, we already have FOUC tests that occasionally flake, probably due to this.


Fixes #12448
Fixes #13058
Fixes #11195
Fixes #10404
@Timer Timer modified the milestones: 9.x.x, iteration 3 Jun 22, 2020
@Timer
Copy link
Member

Timer commented Jun 22, 2020

This should be fixed in next@^9.4.5-canary.15! Please upgrade and let us know.

@showell215
Copy link

Thanks @Timer! 🎉 I tested and the fix works for me.

@jimbuck
Copy link

jimbuck commented Jun 30, 2020

I was experiencing this in both dev mode and production mode in a custom server (incrementally converting pages on enterprise site). ^9.4.5-canary.15 fixed it for me!

@casperle
Copy link

casperle commented Jul 6, 2020

Hi guys. Checked the problem with the canary release, it fixes the issue in dev mode but in production it still remains. Does somebody experiences the same issue?

In the production build there is missing the <style data-next-hide-fouc="true">body{display:none}</style>.

rokinsky pushed a commit to rokinsky/next.js that referenced this issue Jul 11, 2020
We previously used to remove our FOUC helper inside of the style injection to ensure content was shown as fast as possible.

This behavior, however, was problematic for a few reasons:

1. Large JavaScript chunks would take longer than an animation frame to parse, causing FOUC
1. Rendering would sometimes complete before an animation frame, causing improper effects

To fix the latter, we started removing the no FOUC helper **before** rendering, however, we never fixed the former by removing the dead code.

There's not a great way to test this because the FOUC is so fast and flaky, however, this code really shouldn't exist and isn't likely to be re-added (regress).

Also, we already have FOUC tests that occasionally flake, probably due to this.


Fixes vercel#12448
Fixes vercel#13058
Fixes vercel#11195
Fixes vercel#10404
@ozthekoder
Copy link

ozthekoder commented Jul 17, 2020

Hi guys. Checked the problem with the canary release, it fixes the issue in dev mode but in production it still remains. Does somebody experiences the same issue?

In the production build there is missing the <style data-next-hide-fouc="true">body{display:none}</style>.

Same here, is this a different issue when it happens in prod or related, does anyone know?

@showell215
Copy link

Same here, is this a different issue when it happens in prod or related, does anyone know?

For me this issue was only occurring in dev mode, not production mode. So given that, I'd assume the prod issue may have a different cause.

@BlakeBrown
Copy link

Hi guys. Checked the problem with the canary release, it fixes the issue in dev mode but in production it still remains. Does somebody experiences the same issue?

In the production build there is missing the <style data-next-hide-fouc="true">body{display:none}</style>.

Was anyone able to fix this in production? Canary release fixed our dev build, but production is still broken.

@jamesryan-dev
Copy link

so is canary latest version of next then?

@nicholaschiang
Copy link
Contributor Author

Yes @jimmynames, canary is the WIP latest version (the term originated from miners using canary birds to test for toxic fumes... The canary version of Next.js could be sketch).

@Timer
Copy link
Member

Timer commented Jul 31, 2020

This fix is on the stable release of Next.js 9.5.0 and 9.5.1, or newer (applies to development server only)!

@florianmatz
Copy link

What about production? I'm still experiences the FOUC with StyledComponents + Material UI

@praveen7557
Copy link

Did anybody find any solution for this?

@casperle
Copy link

Actually as a quick fix you can do the following:

  1. Add the following css to your page
.no-fouc {
  visibility: hidden;
  opacity: 0;
}

.fouc {
  visibility: visible;
  opacity: 1;
}
  1. Set the no-fouc class to the html element in your app
<html className="no-fouc">
...
</html>
  1. Add this functionality to your App component
...
componentDidMount() {
  const removeFouc = (foucElement) => {
    foucElement.className = foucElement.className.replace('no-fouc', 'fouc');
  };

  removeFouc(document.documentElement);
}
...

This will hide the content of the page until the App component gets fully loaded. At that time all the CSS is parsed already.

@ElegantSoft
Copy link

I happen's to me with latest version

@osequi
Copy link

osequi commented Mar 30, 2021

It's happening to me both in production and dev.

@tgrassl
Copy link

tgrassl commented Mar 30, 2021

Same for me in production with scss modules and next 10.0.8

@anatolzak
Copy link

anatolzak commented May 25, 2021

I am still getting the same problem of unstyled content showing when the page loads in production. I haven't changed _document.js and I only use css modules and I still get FOUC.

Is anyone working on fixing this?

Actually as a quick fix you can do the following:

1. Add the following css to your page
.no-fouc {
  visibility: hidden;
  opacity: 0;
}

.fouc {
  visibility: visible;
  opacity: 1;
}
1. Set the `no-fouc` class to the html element in your app
<html className="no-fouc">
...
</html>
1. Add this functionality to your App component
...
componentDidMount() {
  const removeFouc = (foucElement) => {
    foucElement.className = foucElement.className.replace('no-fouc', 'fouc');
  };

  removeFouc(document.documentElement);
}
...

This will hide the content of the page until the App component gets fully loaded. At that time all the CSS is parsed already.

This is a great solution, but the problem though is that one of the reasons to use nextjs is because it prerenders the page the user is trying to load, which allows the user to see the page way faster because they don't have to wait for the javascript to both load and finish running. This solution is counterproductive to that.

Please fix this

@jamesryan-dev
Copy link

I found I was getting this FOUC because I had miscorrectly used next/head so I can only suggest to anyone experiencing this issue - to try running your code/environment with a stable, starter _document and _app.js to see if this resolves your issue

@anatolzak
Copy link

I was getting FOUC on Firefox in production.

I found out it was a Firefox bug which can be solved by adding a dummy <script> tag right after <body>, like this:

// _document.js

import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
	static async getInitialProps(ctx) {
		const initialProps = await Document.getInitialProps(ctx)
		return { ...initialProps }
	}

	render() {
		return (
			<Html>
				<Head />
				<body>
					<script>0</script>
					<Main />
					<NextScript />
				</body>
			</Html>
		)
	}
}

export default MyDocument

Sounds weird but it did indeed solve the FOUC problem! 🎉

@coultonluke
Copy link

I found I was getting this FOUC because I had miscorrectly used next/head so I can only suggest to anyone experiencing this issue - to try running your code/environment with a stable, starter _document and _app.js to see if this resolves your issue

Please can you tell me how you mis-correctly used it?

@jamesryan-dev
Copy link

from what I can remember I believe I was importing it incorrectly as a standalone item rather than 'next/document'

@coultonluke
Copy link

coultonluke commented Oct 4, 2021

from what I can remember I believe I was importing it incorrectly as a standalone item rather than 'next/document'

Thank you for that. I'm relatively new to nextjs so thanks for that. I'm using the MUI nextjs example and I can see that the document.tsx (or .js in the template) uses Head from next/document and I was using it from next/head. I'm going to test that now so that I confirm the FOUC is no longer happening for me. It was only happening in production (serverless) and not in dev.

The nextjs documentation shows the use of next/head but for a custom document to use Head component from next/document.

Here's some related links in case it helps anyone:
Difference between next/head and next/document Head: #12290
https://nextjs.org/docs/advanced-features/custom-document

@jamesryan-dev
Copy link

I think as Next.js has evolved maybe the correct use / implementation of Head might of changed.. I would always follow / go with whatever next.js examples [most up to date] use as to avoid any issues.. =}

@coultonluke
Copy link

coultonluke commented Oct 4, 2021

I'm still getting FOUC when using the official MUI example.

Their example shows the Head in _document.js from next/document, then the use of next/head in the _app.js, links below:
https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_document.js
https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_app.js

Then I'm using the Head from next/head in one of my own components in /pages to set the <title> element. Strange that it only happens in production though.

@jamesryan-dev
Copy link

could be something else then - I copied a fresh instance of _document.js and _app.js and it resolved my issue.. this might not mean that your issue is the same.. when it comes to high level issues like this I GCSE science it and remove all the pieces and then slowly add again.. good luck I hope this strategy or further googling/comms may help

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.