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

UI breaking with next js #34422

Closed
2 tasks done
mathura333 opened this issue Sep 21, 2022 · 2 comments
Closed
2 tasks done

UI breaking with next js #34422

mathura333 opened this issue Sep 21, 2022 · 2 comments
Labels
examples Relating to /examples status: waiting for author Issue with insufficient information

Comments

@mathura333
Copy link

mathura333 commented Sep 21, 2022

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Steps to reproduce 🕹

Steps:

  1. Added all the code as per the official examples of mui with next js
  2. UI is breaking

Current behavior 😯

UI is breaking

Expected behavior 🤔

No UI break when working with SSR, SSG

Context 🔦

pages/_app.tsx

import React, { useEffect } from 'react';
import type { AppContext, AppInitialProps, AppLayoutProps } from 'next/app';
import type { NextComponentType } from 'next';
import PropTypes from 'prop-types';
import counterpart from 'counterpart';
import hi from 'translations/hi.json';
import en from 'translations/en.json';
import HeadTag from 'components/Root/HeadTag';
import usePageProgressBar from 'hooks/usePageProgressBar';
import { languagesKey } from 'Constants/lang';
import { toast } from 'react-toastify';
import wrapper from 'reduxSM/store';
import { injectStyle } from 'react-toastify/dist/inject-style';
import { CacheProvider, EmotionCache } from '@emotion/react';
import createEmotionCache from 'utils/createEmotionCache';
import { useRouter } from 'next/router';
import {
  Hydrate,
  QueryClient,
  QueryClientProvider,
} from '@tanstack/react-query';
import APP_ENVIRONMENTS from 'Constants/appEnvironments';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import 'styles/globals.scss';
import 'styles/globals.css';
import useInitUserPreferences from 'appHooks/useInitUserPreferences';
import lightTheme from 'theme/light';
import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider } from '@mui/material/styles';

counterpart.registerTranslations(languagesKey.ENGLISH, en);
counterpart.registerTranslations(languagesKey.HINDI, hi);

toast.configure({
  closeOnClick: true,
  autoClose: 3000,
  theme: 'colored',
  limit: 2,
  hideProgressBar: true,
});

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

interface IAppProps extends AppLayoutProps<React.ReactPortal> {
  emotionCache?: EmotionCache;
}

const MyApp: NextComponentType<AppContext, AppInitialProps, AppLayoutProps> = ({
  Component,
  pageProps,
  emotionCache = clientSideEmotionCache,
}: IAppProps) => {
  const [queryClient] = React.useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 300000, // 5 mins
            refetchOnWindowFocus: false,
            retry: 1,
          },
          mutations: {
            retry: 1,
          },
        },
      })
  );
  const router = useRouter();
  useInitUserPreferences();

  useEffect(() => {
    if (typeof window !== 'undefined') {
      if (router.pathname !== '/') {
        injectStyle();
      }
    }
  }, [router.pathname]);

  usePageProgressBar();

  const getLayout = Component.getLayout || ((page: React.ReactNode) => page);

  return (
    <CacheProvider value={emotionCache}>
      <HeadTag />
      <ThemeProvider theme={lightTheme}>
        <CssBaseline />
        <QueryClientProvider client={queryClient}>
          {/* @ts-ignore */}
          <Hydrate state={pageProps.dehydratedState}>
            {/* @ts-ignore */}
            {getLayout(<Component {...pageProps} />)}
            {process.env.NEXT_PUBLIC_APP_ENV !==
              APP_ENVIRONMENTS.PRODUCTION && (
              <ReactQueryDevtools
                initialIsOpen={false}
                position="bottom-right"
              />
            )}
          </Hydrate>
        </QueryClientProvider>
      </ThemeProvider>
    </CacheProvider>
  );
};

MyApp.propTypes = {
  Component: PropTypes.func.isRequired,
  pageProps: PropTypes.shape({}).isRequired,
};

// * Wrapper created using next-redux-wrapper
export default wrapper.withRedux(MyApp);

createEmotionCache util

import createCache from '@emotion/cache';

const isBrowser = typeof document !== 'undefined';

// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
// This assures that MUI styles are loaded first.
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
export default function createEmotionCache() {
  let insertionPoint;

  if (isBrowser) {
    const emotionInsertionPoint = document.querySelector(
      'meta[name="emotion-insertion-point"]'
    );
    insertionPoint = emotionInsertionPoint ?? undefined;
  }

  return createCache({ key: 'mui-style', insertionPoint, prepend: false });
}

_document.tsx

import * as React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '@emotion/server/create-instance';
import createEmotionCache from '../utils/createEmotionCache';
import lightTheme from 'theme/light';

export default class MyDocument extends Document {
  render() {
    return (
      <Html lang="en">
        <Head>
          <meta charSet="utf-8" />
          <meta name="theme-color" content={lightTheme.palette.primary.main} />
          <link rel="preconnect" href="https://fonts.googleapis.com" />
          <link
            rel="preconnect"
            href="https://fonts.gstatic.com"
            crossOrigin="anonymous"
          />
          <link
            rel="stylesheet"
            href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css"
          />
          <link
            href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;500;600;700&display=swap"
            rel="stylesheet"
          />
          <link rel="preconnect" href="https://neuro-node.herokuapp.com" />
          {/* Inject MUI styles first to match with the prepend: true configuration. */}
          {(this.props as any).emotionStyleTags}
        </Head>
        <body>
          <Main />
          <div id="huvile-portal" />
          <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);

  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App: any) =>
        function EnhanceApp(props) {
          return <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-org/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,
  };
};

package.json dependencies

"@emotion/cache": "^11.9.3",
"@emotion/react": "^11.9.3",
"@emotion/server": "^11.4.0",
"@emotion/styled": "^11.9.3",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@mui/icons-material": "^5.8.4",
"@mui/lab": "^5.0.0-alpha.86",
"@mui/material": "^5.8.4",
"@mui/styles": "^5.8.4",
"@mui/system": "^5.8.4",
"next": "^12.3.0",
"react": "^18.1.0",
"react-dom": "^18.1.0"

Your environment 🌎

npx @mui/envinfo
  Don't forget to mention which browser you used.
  Output from `npx @mui/envinfo` goes here.
@mathura333 mathura333 added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Sep 21, 2022
@ZeeshanTamboli
Copy link
Member

Please provide a CodeSandbox (https://mui.com/r/issue-template), a link to a repository on GitHub that we can fork that reproduces the problem.

It's hard to check without it.

@ZeeshanTamboli ZeeshanTamboli added status: waiting for author Issue with insufficient information examples Relating to /examples and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Sep 23, 2022
@github-actions
Copy link

Since the issue is missing key information and has been inactive for 7 days, it has been automatically closed. If you wish to see the issue reopened, please provide the missing information.

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

No branches or pull requests

2 participants