Loading a .css file in advance using NextJS

We have integrated NextJS and Material-UI into our website, but we are facing an issue with FOUC (Flash of Unstyled Content) upon page loading. After some investigation, I discovered that the JS files are loading faster than the CSS file, causing the problem. Is there a way to preload the CSS file? All our pages use the same CSS file located at /pages/styles.css

If it helps, here is the content of /pages/_app.js:

// 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>
  )
}

Answer №1

It seems that the styles may not have been applied on the server-side. To resolve this, consider adding _document.js from Material-UI's Next.js example and customize it according to your requirements.

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

You can also try removing server-side injected CSS in the _app.js as shown in the example:

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

Answer №2

An alternative solution is to hide the body element by using display: none; until the CSS file has finished loading, and then change it to display: block;

Answer №3

To ensure optimal performance, it is recommended to load the CSS file using the <link> element within the head section of your HTML document. This way, the browser's parsing process can prioritize loading the CSS file before rendering the site content.

If you are currently loading the CSS file using JavaScript after the First Contentful Paint (FCP), there are two solutions to address this issue:

  1. Follow the standard approach by linking the CSS file with a <link> element as mentioned above.
  2. Alternatively, you can retrieve the text content of the CSS file and dynamically set it as the innerHTML of a <style> element.

Answer №4

Dealing with the same problem, I found a solution. I successfully resolved it by inserting the following code snippet into _document.tsx

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The Bootstrap Tooltip seems to be glued in place

Utilizing jQuery, I am dynamically generating a div that includes add and close buttons. Bootstrap tooltips are applied to these buttons for additional functionality. However, a problem arises where the tooltip for the first add button remains visible even ...

Switch between playing and pausing the mp3 audio in the Next application by using the toggle

I am currently working on my website and I have been trying to add a button to the bottom of the page that can play or pause a song using useSound library. The song starts playing when I click it for the first time, however, I am facing difficulty in stopp ...

Revamped Web Presentation: The Dynamic Blend

I'm struggling to arrange a section in the same way as shown in this image. https://i.stack.imgur.com/QVDHn.png I have been using bootstrap 4 and have experimented with rows and columns, but haven't been able to achieve the desired layout. Curr ...

Monitor modifications to documents and their respective sub-collections in Firebase Cloud Functions

Is it possible to run a function when there is a change in either a document within the parent collection or a document within one of its subcollections? I have tried using the code provided in the Firebase documentation, but it only triggers when a docume ...

A situation where the event onclick fails to occur within NextJS

index.js: function Home () { return <div> <html> <head> <title>Site</title> </head> <body> <div class= 'v5_3' onclick = "func_click()"></div> </b ...

How to direct all wildcard paths to a particular route in Next.js

I currently have a single landing page application built with nextJs. I am wondering if it is possible to redirect all paths to specific routes, similar to how we do it in react-router. How can I achieve the same functionality in nextJs? <BrowserRou ...

A guide to implementing infinite scrolling with vue-infinite-loading in Nuxt.js (Vue.js)

Currently, I am working on developing a web application using Nuxt.js (Vue.js). Initially, to set up the project, I used the command: vue init nuxt/express MyProject ~page/help.vue <template> <div> <p v-for="item in list"> ...

Is it possible to nest CSS Variables within CSS Variable names?

Is it feasible to embed a CSS Variable inside another CSS variable's name or perform a similar action like this: :root { --color-1: red; --index: 1; } span { color: var(--color-var(--index)) } ...

what is the best way to display camera view in full screen on an iOS application?

What method should I use on an iPhone to launch the camera in full screen mode? Additionally, how can I overlay a compass on top of the camera view on iOS? ...

Stable and persistent popup window that remains at the forefront in a Chrome extension

Currently developing a Google Chrome extension and seeking assistance in creating a popup window that remains fixed in one corner while staying on top of all other windows. Here is a reference image for clarification: https://i.stack.imgur.com/IPw7N.jpg ...

Component inexplicably rendering multiple times

After encountering multiple re-renders in my code, I decided to comment out every line and discovered that the constant definition was causing the issue: const MyComponent = () => { console.log('render') // logs 4 times const myRef = useR ...

Scraping Secrets: Unraveling the Art of Procuring User Links

I need assistance with extracting links from the group members' page on Meetup: response.css('.text--ellipsisOneLine::attr(href)').getall() Can you please help me understand why this code is not functioning correctly? This is the HTML stru ...

Is Cloud Spanner effectively handling session management?

I've spent some time researching this issue but unfortunately haven't been able to find a satisfactory answer. The Google Cloud Spanner client libraries automatically handle sessions, with a limit of 10,000 sessions per node. So far, so good. M ...

Developing a hybrid SSG using NextJS and Magnolia CMS technology

Recently, I've been delving into the Magnolia CMS NextJS hybrid SSG project. There's a boilerplate code for a page called pages/[[pathname]] that effectively generates static content and paths for all pages added in the admincentral app. It works ...

PHP and JavaScript are two powerful programming languages that are

While I understand that PHP and JavaScript operate in different locations, I am curious to know if there is a way to incorporate some PHP code into my JavaScript file. I need to create unique URLs for linking to profiles and news posts, such as /#/news/IDH ...

Encountering an issue with the history module when utilizing the webpack dev server

I am encountering an issue while trying to run webpack dev server. The history functionality was working fine until I started using the webpack module. A warning message appeared in my console: WARNING in ./src/history.js 2:15-35 export 'createBrows ...

The button now has a stylish 5px border, making it slightly larger in size. However, it seems

How can I add an active class with a 5px border-bottom on a button without increasing the button size by 5px? The box-sizing property is not working for me. Is there a simple solution to achieve this? *, *:after, *::before { -webkit-box-sizing: border ...

React - triggering a router link action before the link is assigned a value

In this scenario, the issue arises because the URL changes before the link receives a value. The state is being received directly through the component but by the time it happens, the component has already been clicked. This results in the value being as ...

Fixing the department display list in React Hook: A step-by-step guide

{ roomDept.map((item, index) => ( <div key={index} className="flex flex-col pb-2 items-center"> <div className="flex pb-2 w-full"> <SelectPick ...

What could be causing the defaultSelectedKeys property in the ant design Menu component to not update when changes are made

Hello there, I'm facing an issue with the Menu component from ant-design. It seems that the menu is not detecting the state changes that are happening. Here's what I am trying to do: when a button is clicked, I want to change the defaultSelected ...