简体   繁体   中英

Preloading a .css file with NextJS

We are using NextJS and Material-UI for our site, and upon loading the pages, it gives a FOUC. I've narrowed the problem down to the fact that the JS loads faster than the.css file, so I was wondering if there was a way to preload the.css file? All of our pages use the same.css file which is located under /pages/styles.css

Here is /pages/_app.js if that's any help:

// pages/_app.js
import { Provider } from 'next-auth/client'
import { createMuiTheme } from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/styles';
import styles from './styles.css'

import Layout from '../components/layout'
import Head from 'next/head'

const theme = createMuiTheme({
  palette: {
    primary: {
      main: "#2196f3", // blue
    },
    secondary: {
      main: "#d3d3d3", // gray
    },
  },
});

export default function _App ({ Component, pageProps }) {
  return (
    <ThemeProvider theme={theme}>
      <Provider options={{ clientMaxAge: 0, keepAlive: 0 }} session={pageProps.session}>
        <Layout>  
          {/* Head */}
          <Head>
            <title>Kevin Support</title>
            <link rel="icon" href="/static/favicon.png"/>
          </Head>

          {/* Page */}
          <Component {...pageProps} />
        </Layout>
      </Provider>
    </ThemeProvider>
  )
}

Load the CSS file with the <link> element inside the head. The parsing process of the browser will then make sure that the CSS file is loaded before the site content is shown.

In your current approach you load the CSS is loaded with JavaScript, after the FCP has rendered the CSS will be parsed.

You have 2 options to fix this:

  1. You link the CSS file as mentioned above with a <link> element.
  2. You get the text content of the CSS file and set it as the innerHTML of a <style> element.

Perhaps styles weren't applied on the server-side. Try to add _document.js from Material-UI's Next.js example . Adjust it to your needs.

// pages/_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 '../src/theme';

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

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side 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

  // 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()],
  };
};

Also, you can try to remove server-side injected CSS in the _app.js like so (see example ):

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

I faced this same issue. what I did was to make this work was to add this script inside _document.tsx

     <script
            dangerouslySetInnerHTML={{
              __html: `
              if(document) {
            document.querySelectorAll("link[rel='preload'][as='style']").forEach(link => link.rel = "stylesheet")}
            `
            }}
          />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM