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

tips for transferring each div to a different location

I am looking to rearrange the div.my-div from containers into sections. How can I achieve this for each section? <section> <div class="container">container <div class="my-div">my div</div> </div> </section&g ...

Sometimes, it feels like TypeScript's async await does not actually wait for the task to complete before moving on

Recently, I have been transitioning to using the async await pattern more frequently instead of the traditional Promise syntax because it can help in keeping the code structure cleaner. After some experimentation, I felt like I had a good grasp on how to u ...

Error in NextJS: The hydration process failed due to discrepancies between the initial UI and the server-side rendering

Error An Unexpected Error Has Occurred Error: Unable to hydrate the initial UI as it does not match the server-rendered content. For more information, please visit: https://nextjs.org/docs/messages/react-hydration-error I'm puzzled by this error me ...

Sending array values from a dynamic input field in Angular 4 and processing them accordingly

I am currently exploring options on how to add and delete multiple input fields. When a user submits two or more fields, I want the results to be displayed in an array. This is my HTML form: <form method="post" [formGroup]="formData"> ...

Combining routes in Express and Angular can be achieved by leveraging the power

How can I successfully integrate Jade AngularJS with ExpressJS? I have an app.js file for Express to run the server using Grunt. This app.js file renders home.jade which leads me to the home page. On the homepage, I have implemented AngularJS. I also creat ...

What is the best way to activate multiple events within an overlapping area containing multiple divs at the same

How can I trigger events on same level divs that overlap in different areas? When there are multiple divs overlapping, only one of them gets triggered. Is there a way to trigger events on all overlapped same level divs? Here is an example code snippet: ...

Developing a customizable datepicker with the ability to select specific months or date ranges

I am currently developing a WebApp using flask and constructing templates in HTML/JS for the front end. I am in need of a datepicker that will provide the user with the option to choose a specific date range or select a range of months. Take a look at the ...

Activate event starting from parent and ascending through child nodes

When I have a table inside a <div class="timely"></div>, and within that table is a <th class="prev"><i>previous</i></th>, Chrome developer tools show an event listener on the <th>. However, Firefox developer tools ...

Component fails to update when state updated using useState()

In my current project, I am facing an issue with a parent (App) and child (MUIDatatable) component setup. The child component is a datatable that requires a columns prop to define the structure of the columns, including a custom render function for one of ...

Leveraging highland.js for sequentially executing asynchronous functions while maintaining references to the initial stream data

I am dealing with a series of events: var eventStream = _([{ id: 1, foo: 'bar' }, { id: 2, foo: 'baz' }]); My task is to load an instance of a model for each event in the stream (my Data Access Layer returns promises) and then tri ...

Learn how to incorporate a YouTube video into your website without using Flash or JavaScript by utilizing a popup window

I am in search of assistance with coding for my website to include a video popup feature featuring a YouTube video. I prefer the use of HTML5 rather than Flash or JavaScript. Unfortunately, I have been unable to locate any suitable code examples. While I ...

Constructing a table in React using columns instead of the traditional rows

Currently tackling a project in react, I am looking to construct a material-ui table with specific characteristics. Within my array of elements, each element represents a column in the table and contains a name and the number of cells it covers. For examp ...

What are the steps for loading JSON data into a select dropdown with the help of AJAX?

I am trying to create a dropdown list of schools using the select tag. Currently, I have hard coded values for the list, but I want to fetch the data from a RESTful service instead. Can someone please provide guidance on how to achieve this? <html& ...

Handling Camera Positioning and Direction in Three.js: Utilizing Orbit Controls and camera

Having trouble getting Orbit Controls to function correctly after setting the camera to : camera.up.set(0,0,1) This results in improper orbiting behavior, and there are some unresolved questions online addressing this issue: Three.js: way to change the u ...

Obtaining response object when encountering 401 error in AngularJS

I am currently working with Angular 1.6.4, Express 4.15.2, and express-session. My goal is to identify whether a user is unauthorized to access a specific route by checking for the existence of the req.session.user parameter. If the user is not authorized, ...

The best practices for utilizing ES6 Modules syntax in TypeScript files within a NextJS environment

The issue appears to be trapped in a loop: package.json is missing type: "module". This results in an error when trying to use modules in TypeScript files: An error occurred while running the seed command: /Users/me/code/me/prisma-learning/grap ...

Tips for showcasing two cards side by side on mobile screens?

This is my glitch. I have a layout where I see 3 cards in a row for desktops, 2 cards per row on tablets, and 1 card for mobile devices. However, I would like the cards to resize and display 2 cards in a row on mobile. How can I achieve this? Here is the f ...

Utilizing JavaScript to Load and Showcase Images from a Temporary Image to imgsrc

How can I dynamically display images from the temporary image array 'tempimages' onto the 'img' element with the 'src' property? I have written a code that attempts to display temporary images from 'tempimages[]' on ...

Changing the Flash message to an Alert message in Symfony2: A step-by-step guide

I've encountered a problem where the success message from an action method in Symfony2 Controller appears as a flash message, but I need to display it as an alert or dialogue message according to requirements. I have attempted various solutions witho ...

I prefer to have the boxes lined up horizontally, but they seem to be arranged vertically

I'm struggling to get the color boxes to display horizontally with spaces between them. Currently, they are showing up vertically in a column layout. I want 4 smaller color boxes within a larger grey box at the top layer, with margins and padding app ...