6

I'm building a react app using material ui and nextjs. I'm using <Autocomplete /> component, provided by material UI and override some its styles with my own like this:

<Autocomplete
  classes={{
    root: `${styles[`search__autocomplete`]} ${
      styles[`search--${variant}__autocomplete`]
    }`,
    inputRoot: `${styles[`search__autocomplete-input`]} ${
      styles[`search--${variant}__autocomplete-input`]
    }`
  }} 
/>

variant is a prop, which gets passed to the component and styles is a variable, which get imported from the css module: import styles from "Search.module.sass".

Now, when I'm working on this locally everything looks great:

expected

But, after I deploy it to production via next build && next export I start experiencing "flickering" effect when for like 1/3 of a second my page looks like this:

enter image description here

My guess is that it might be related to the fact that nextjs export my css to several files on production:

    <link
      rel="preload"
      href="/_next/static/css/4dcd7fa805fb41261f08.css"
      as="style"
    />
    <link
      rel="stylesheet"
      href="/_next/static/css/4dcd7fa805fb41261f08.css"
      data-n-g=""
    />
    <link
      rel="preload"
      href="/_next/static/css/a23cf79bceae4047fddb.css"
      as="style"
    />
    <link
      rel="stylesheet"
      href="/_next/static/css/a23cf79bceae4047fddb.css"
      data-n-p=""
    />

How can I solve that?

3 Answers 3

5

The solution was to create a custom pages/_document.js page with:

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

class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>

          <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;700&display=swap" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side rendering (SSR).
MyDocument.getInitialProps = async (ctx) => {
  // Resolution order
  //
  // On the server:
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. document.getInitialProps
  // 4. app.render
  // 5. page.render
  // 6. document.render
  //
  // On the server with error:
  // 1. document.getInitialProps
  // 2. app.render
  // 3. page.render
  // 4. document.render
  //
  // On the client
  // 1. app.getInitialProps
  // 2. page.getInitialProps
  // 3. app.render
  // 4. page.render

  // Render app and page and get the context of the page with collected side effects.
  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 fragment is rendered after the app and page rendering finish.
    styles: [
      ...React.Children.toArray(initialProps.styles),
      sheets.getStyleElement(),
    ],
  };
};

export default MyDocument;
Sign up to request clarification or add additional context in comments.

Comments

0

In my case it was caused by the <CacheProvider> component of @emotion/react removing it from _app.js file fixed the problem for me

Nextjs is now supporting SSR with emotion you can enable it this way in next.config.js file :

const nextConfig = {
  //...
  compiler: {
    emotion: true,
  },
};

Also if using <CssBaseline/> component of Material UI replace it with <ScopedCssBaseline>

However, you might be progressively migrating a website to MUI, using a global reset might not be an option. It's possible to apply the baseline only to the children by using the ScopedCssBaseline component.

import { ScopedCssBaseline } from "@mui/material";
//...
<ScopedCssBaseline>
  <Component {...pageProps} />
</ScopedCssBaseline>
//...

Comments

0
import React, { useState, useEffect } from 'react';

interface IFadeInProps {
  children: React.ReactNode;
}

function FadeIn(props: IFadeInProps) {
  const [show, setShow] = useState(false);

  useEffect(() => {
    setShow(true);
  }, []);

  return (
    <div style={{ opacity: show ? 1 : 0 }}>
      {props.children}
    </div>
  );
}

export default FadeIn;

I use tailwindcss in next.js. Use this to fix it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.