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

mui 5 causes icon flashing on initial page load with next js #34652

Closed
2 tasks done
StarAzure opened this issue Oct 7, 2022 · 12 comments
Closed
2 tasks done

mui 5 causes icon flashing on initial page load with next js #34652

StarAzure opened this issue Oct 7, 2022 · 12 comments
Assignees
Labels
package: icons Specific to @mui/icons status: waiting for author Issue with insufficient information

Comments

@StarAzure
Copy link

StarAzure commented Oct 7, 2022

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Steps to reproduce 🕹

Steps:

  1. material ui 5 with next js 12.1.5 or 12.1.6 or even latest 12.3.1
  2. page on initial load - icons flash as a huge icon on page load
  3. also followed example - https://github.com/mui/material-ui/tree/master/examples/nextjs

This was discussed briefly here but still happens - #32830

Current behavior 😯

Huge icons flash with nextjs

Example - https://fontawesome.com/v6/docs/web/use-with/react/use-with#next-js

Expected behavior 🤔

normal icons size

Context 🔦

large icons shows on initial page load

Your environment 🌎

next js 12.1.5 or 12.3.1
react 17.0.2
mui latest version 5 installed this week

@StarAzure StarAzure added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Oct 7, 2022
@StarAzure
Copy link
Author

Also described here but was never fixed - #32775

@mnajdova
Copy link
Member

Is this related to font awesome icons only, or SVG icons too? Can you link a github repository with a reproduction?

@mnajdova mnajdova added status: waiting for author Issue with insufficient information package: icons Specific to @mui/icons and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Oct 11, 2022
@StarAzure
Copy link
Author

@mnajdova I do not have a public repo for this so I will have to create one that can reproduce this.

I have however added the 3 important file - app.js, document.js and my Layout.js
appjs:

import React from 'react';
import PropTypes from 'prop-types';
import Head from 'next/head';
import { CacheProvider } from '@emotion/react';
import CssBaseline from '@mui/material/CssBaseline';
import Layout from '../components/layout';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import LinearProgress from '@mui/material/LinearProgress';
import * as gtag from '../lib/googleAnalytics';
import '../styles/globals.css';
import createEmotionCache from '../components/theme/createEmotionCache';

// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();

export default function NCD(props) {
  const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
  const [isLoading, setLoadingState] = useState(false);

  const router = useRouter();

  useEffect(() => {
    const handleRouteChange = (url) => {
      gtag.pageview(url)
    }
    // Logging to prove _app.js only mounts once,
    // but initializing router events here will also accomplishes
    // goal of setting state on route change
    router.events.on('routeChangeStart', () => {
      setLoadingState(true);
    });

    router.events.on('routeChangeComplete', () => {
      handleRouteChange;
      setLoadingState(false);
    });

    router.events.on('routeChangeError', () => {
      setLoadingState(false);
    });

    if (typeof window == 'undefined') {
      setLoadingState(true);
    }

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

  return (
    <React.Fragment>
      <Head>
        <title>{process.env.SITENAME}</title>
        <meta name="viewport" content="initial-scale=1, width=device-width" />
      </Head>
      <CacheProvider value={emotionCache}>
      {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
      <CssBaseline />
        <Layout>
      {isLoading? (  
        <>
          <div className="pageLoader">
            <LinearProgress />
          </div>
         </>):(
          <>

          <Component {...pageProps} />
        
         </>
        )}
        </Layout>
        </CacheProvider>
    </React.Fragment>
  );
}

NCD.propTypes = {
  Component: PropTypes.elementType.isRequired,
  pageProps: PropTypes.object.isRequired,
};

document.js


import * as React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '@emotion/server/create-instance';
import createEmotionCache from '../components/theme/createEmotionCache';
import { GA_TRACKING_ID } from '../lib/googleAnalytics';
import ServerStyleSheets from '@mui/styles/ServerStyleSheets';

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

          <link rel="shortcut icon" href="/favicon.ico" />
          <link
            rel="stylesheet"
            href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
          />
          {/* Global Site Tag (gtag.js) - Google Analytics */}
          <script
            async
            src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
          />
          <script
            dangerouslySetInnerHTML={{
              __html: `
            window.dataLayer = window.dataLayer || [];
            function gtag(){dataLayer.push(arguments);}
            gtag('js', new Date());
            gtag('config', '${GA_TRACKING_ID}', {
              page_path: window.location.pathname,
            });
          `,
            }}
          />

        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with static-site generation (SSG).
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

  const originalRenderPage = ctx.renderPage;

  // You can consider sharing the same Emotion cache between all the SSR requests to speed up performance.
  // However, be aware that it can have global side effects.
  const cache = createEmotionCache();
  const { extractCriticalToChunks } = createEmotionServer(cache);

  const sheets = new ServerStyleSheets()

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

  const initialProps = await Document.getInitialProps(ctx);
  // This is important. It prevents Emotion to render invalid HTML.
  // See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153
  const emotionStyles = extractCriticalToChunks(initialProps.html);
  const emotionStyleTags = emotionStyles.styles.map((style) => (
    <style
      data-emotion={`${style.key} ${style.ids.join(' ')}`}
      key={style.key}
      // eslint-disable-next-line react/no-danger
      dangerouslySetInnerHTML={{ __html: style.css }}
    />
  ));

  return {
    ...initialProps,
    emotionStyleTags,
  };
};

Layout.js


import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import {lightTheme, darkTheme} from '../components/theme';
import TopAppBar from './ui/topAppBar';
import { useCookies } from 'react-cookie';
import Copyright from '../components/ui/copyright';
import Box from '@mui/material/Box';

const layout = ({ children }) => {
  const [cookies, setCookie] = useCookies(['darkTheme']);

  return <>
    <StyledEngineProvider injectFirst>
      <ThemeProvider theme={cookies.darkTheme=="true" ? darkTheme : lightTheme}>
        <CssBaseline />
      
        <header>
          <nav>
          <TopAppBar />
          </nav>
        </header>

          <main>{children}</main>
      <Box mt={8} mb={4} >
        <Copyright />
      </Box>
      </ThemeProvider>
    </StyledEngineProvider>
  </>;
};

export default layout;

@github-actions github-actions bot removed the status: waiting for author Issue with insufficient information label Oct 12, 2022
@StarAzure
Copy link
Author

@mnajdova The suggestion I found on stackoverflow is to load the mui icons upfront. How can I do this for mui icons?

For mui, I import one icon at a time
Example:
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';

How can I do a global import of mui icons upfront and prevent reimport?

@mnajdova
Copy link
Member

Maybe try forking the official example and start from there. The issue is not happening there - https://stackblitz.com/github/mui/material-ui/tree/master/examples/nextjs?file=README.md

@mnajdova mnajdova added the status: waiting for author Issue with insufficient information label Oct 12, 2022
@StarAzure
Copy link
Author

StarAzure commented Oct 12, 2022

@mnajdova Thanks. Followed the example for app, document js but same issue. Has this anything to do with how emotion css is loaded first at the top before even page title?

image

@github-actions github-actions bot removed the status: waiting for author Issue with insufficient information label Oct 12, 2022
@StarAzure
Copy link
Author

StarAzure commented Oct 12, 2022

I fixed the issue with a hack in my high level code but I don't think this is the efficient way. It probably defeats the purpose of SSR

Basically I wrapped my layout and everything around useEffect. thoughts??

I used useEffect but thinking about using https://reactjs.org/docs/hooks-reference.html#uselayouteffect


  useEffect(() => {
    setLoading(false);
  }, []);
  const toReturn = (<>
    <StyledEngineProvider injectFirst>
...
...
    </StyledEngineProvider>
  </>);
  if (!loading) {
    return toReturn
  }

@mnajdova
Copy link
Member

mnajdova commented Oct 14, 2022

I am closing the issue, there is no clear reproduction, it works on the example project. Feel free to re-open in case if you create a reproduction repository.

@mnajdova mnajdova added the status: waiting for author Issue with insufficient information label Oct 14, 2022
@Wellers0n
Copy link

Screenshot 2023-01-16 at 19 22 52

IMG_3045.mov

I have this problem.

@guansss
Copy link

guansss commented Feb 15, 2023

Looks like <StyledEngineProvider> is conflicting with <CacheProvider>. Just remove the <StyledEngineProvider> and the problem is gone.

To keep the effect of the injectFirst option, create your Emotion cache as follows:

const cache = createCache({
  key: 'css',
  prepend: true,
});

This is memtioned in the docs: https://mui.com/material-ui/guides/interoperability/#tailwind-css

Edit: placing <StyledEngineProvider> above <CacheProvider> also seems to work:

<StyledEngineProvider injectFirst>
  <CacheProvider value={emotionCache}>
    ...
  </CacheProvider>
</StyledEngineProvider>

@iamPedram1
Copy link

iamPedram1 commented Oct 22, 2023

any updates for this issue? i have the same issue on this version 👇
`
"@emotion/cache": "^11.11.0",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.14",
"@mui/material": "^5.14.14",
"next": "13.5.6",

`

when page loads icons are so huge and then after 1 sec it goes back to original size.

@lucasvieirasilva
Copy link

+1, same here, when the page loads icons are so huge, and then after 1 sec it goes back to its original size.

next.js: 14.2.22
mui: 6.1.2

I'm using the AppRouterCacheProvider as recommended in the MUI docs (https://mui.com/material-ui/integrations/nextjs/)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package: icons Specific to @mui/icons status: waiting for author Issue with insufficient information
Projects
None yet
Development

No branches or pull requests

6 participants