next.jsmaterial-uifouc

fouc problem with nextjs and material ui v4 loads a raw html first


I recently migrated to nextjs. I'm using material v4 and next version 10.2 I've tried the official example in material ui site to prevent loading the raw html first by adding the _document.js file https://github.com/mui/material-ui/tree/v4.x/examples/nextjs so far my problem still exists. This is my _document.js

import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '@material-ui/core/styles';
import theme from '../assets/theme';

class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          <meta name="theme-color" content={theme.palette.primary.main} />
          <link
            rel="stylesheet"
            href="https://fonts.googleapis.com/css?family=Lato:300,400,500,700&display=swap"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

MyDocument.getInitialProps = async ctx => {
  const sheets = new ServerStyleSheets();
  const originalRenderPage = ctx.renderPage;

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: App => props => sheets.collect(<App {...props} />),
    });

  const initialProps = await Document.getInitialProps(ctx);

  return {
    ...initialProps,

    styles: [
      ...React.Children.toArray(initialProps.styles),
      sheets.getStyleElement(),
    ],
  };
};
export default MyDocument;

This is my _app.js

import React from 'react';
import PropTypes from 'prop-types';
import Head from 'next/head';
import { ThemeProvider } from '@material-ui/core/styles';

import theme from '../assets/theme';
import withMui from '../components/withMui';
import store from '../utils/store';
import { makeStyles } from '@material-ui/core/styles';
import { Provider } from 'react-redux';
import Ribbon from '../components/RibbonComponent';
const useStyles = makeStyles(() => ({
  '@global': {
    '*::-webkit-scrollbar': {
      width: '0.4em',
    },
    '*::-webkit-scrollbar-track': {
      '-webkit-box-shadow': 'inset 0 0 6px rgba(0,0,0,0.00)',
    },
    '*::-webkit-scrollbar-thumb': {
      backgroundColor: 'rgba(0,0,0,.1)',
    },
  },
  appWrapper: {
    width: '100%',
    height: '100%',
    display: 'flex',
    flexDirection: 'column',
  },
  appContent: { flex: 1 },
}));
function MyApp(props) {
  const { Component, pageProps } = props;

  const classes = useStyles();
  React.useEffect(() => {
    // Remove the server-side injected CSS.
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return (
    <React.Fragment>
      <Head>
        <title>Tisitano</title>
        <link
          rel="stylesheet"
          href="https://fonts.googleapis.com/css?family=Lato:300,400,500,700&display=swap"
        />
        <meta
          name="viewport"
          content="minimum-scale=1, initial-scale=1, width=device-width"
        />
        <script src="https://www.gstatic.com/firebasejs/5.5.3/firebase-app.js"></script>
        <script src="https://www.gstatic.com/firebasejs/5.5.3/firebase-firestore.js"></script>
        <script
          strategy="afterInteractive"
          dangerouslySetInnerHTML={{
            __html: `myfacebookKeyAndrelatedHtml`,
          }}
        />
      </Head>
      <ThemeProvider theme={theme}>
        <Provider store={store}>
          <Ribbon />
          <div className={classes.appWrapper}>
            <div className={classes.appContent}>
              <Component {...pageProps} />
            </div>
          </div>
        </Provider>
      </ThemeProvider>
    </React.Fragment>
  );
}

MyApp.propTypes = {
  Component: PropTypes.elementType.isRequired,
  pageProps: PropTypes.object.isRequired,
};
export default withMui(MyApp);

And my index.js

import React from 'react';

import Home from './home';

import MainFooter from '../components/home/mainFooter';

function Index() {
  return (
    <>
      <Home /> <MainFooter />
    </>
  );
}
export default Index;

My withMui.js file

import React, { Component } from 'react';

import { ThemeProvider } from '@material-ui/core/styles';
import { createTheme } from '@material-ui/core/styles';
import myTheme from '../assets/theme';

const muiTheme = myTheme;

export default function outputComponent(NextPage) {
  class outputComponent extends Component {
    constructor(props) {
      super(props);
      this.state = {
        nav: '',
      };
    }
    componentDidMount() {
      this.setState(state => {
        state.nav = navigator.userAgent;
        return state;
      });
    }
    static async getInitialProps(ctx) {
      const { req } = ctx;

      const userAgent = req ? req.headers['user-agent'] : this?.state?.nav;
      let pageProps = {};
      if (NextPage.getInitialProps) {
        pageProps = await NextPage.getInitialProps(ctx);
      }

      return {
        ...pageProps,
        userAgent,
      };
    }
    render() {
      let userAgent = this.props.userAgent;
      return (
        <ThemeProvider theme={createTheme({ userAgent, ...muiTheme })}>
          <NextPage {...this.props} />
        </ThemeProvider>
      );
    }
  }

  return outputComponent;
}

Here is what I tried so far:

  1. I tried removing withMui file and still I saw the raw html. by the way without withmui file all my styles break.

  2. I tried mixing the _document.js with my withMui file to have them both on server side it didn't work.

  3. I also made sure that server restarts.

  4. I even saw on a github link to add a dummy script(which was not convincing but I tried it) <script>0</script> but this didn't work either.

  5. I added getInitialProps to my _app.js.

  6. I tried removing the ribbon and wrapper divisions from _app.js and adding them to index file it didn't work.

I can not update my material or next version due to dependency conflict for now so if anyone knows any workaround on these versions please help me.


Solution

  • I solved my issue. I downloaded the official project that material represents for next js, and there were structural difference with my project. I didn't have a src folder to store my theme and made some changes to the index.js as well.

    this is my index:

    export default function Index() {
      return (
        <>
       <mainComponent/>
        </>
      );
    }
    

    Also I moved all files from root of the pages to folders and I kept only _app.js, _document, 404 and index.

    I had theme and provider this is my _app.js

     <Head>
    ...
     </Head>
    
          <ThemeProvider theme={theme}>
            <Provider store={store}>
              <Ribbon />
              <Component {...pageProps} />
            </Provider>
          </ThemeProvider>
        </React.Fragment>
    

    I also removed withmui file its not needed.