Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to defer load render blocking css in next.js?

As you can see in the below next.js code I am trying to defer load the render blocking css by giving my main.css file path in href attribute but I am struggling to do it in next.js. What I want is after loading the critical css in _document.js tag under tag, to load the non-critical css which is not above the fold.

_app.js

import App from "next/app"
import Head from "next/head"
import React from "react"
import { observer, Provider } from 'mobx-react'
import Layout from "../components/Layout"
import allStores from '../store'

export default class MyApp extends App {
componentDidMount = () => {
       
};
render() {
        const { Component, pageProps, header, footer,  } = this.props
        return (
            <>
                <Head >
                    <link rel="preload" href="path/to/main.css" as="style" 
                    onLoad="this.onload=null;this.rel='stylesheet'"></link>
                </Head>
                <Provider {...allStores}>
                    <Layout header={header}  footer={footer}>
                        <Component {...pageProps} />
                    </Layout>
                </Provider>
            </>
        )
    }
}
like image 820
Aniket Jais Avatar asked Aug 03 '20 04:08

Aniket Jais


People also ask

Can I use defer with CSS?

Defer loading a CSS script gives you the ability to load CSS files after your web page (the DOM) has fully finished loading.


2 Answers

as @chrishrtmn said at _document.js you can do like this:

import Document, { Main, NextScript } from 'next/document';
import { CriticalCss } from '../components/CriticalCss';

class NextDocument extends Document {
  render() {
    return (
      <html>
        <CriticalCssHead />
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

export default NextDocument;

as in your component you can put the CSS:

import { readFileSync } from 'fs';
import { join } from 'path';

export interface Props {
  assetPrefix?: string;
  file: string;
  nonce?: string;
}

export const InlineStyle: React.FC<Props> = ({ assetPrefix, file, nonce }) => {
  const cssPath = join(process.cwd(), '.next', file);
  const cssSource = readFileSync(cssPath, 'utf-8');
  const html = { __html: cssSource };
  const id = `${assetPrefix}/_next/${file}`;
  return <style dangerouslySetInnerHTML={html} data-href={id} nonce={nonce} />;
};

I got the source for this code from the current repo:

https://github.com/JamieMason/nextjs-typescript-tailwind-critical-css

have a look here

https://github.com/JamieMason/nextjs-typescript-tailwind-critical-css/tree/master/components/CriticalCssHead

like image 93
Ruben Marcus Avatar answered Nov 13 '22 00:11

Ruben Marcus


Here's my current favorite solution, sourced from here: https://github.com/facebook/react/issues/12014#issuecomment-434534770

It results in two empty <script></script> tags in your head, but works.

<script
  dangerouslySetInnerHTML={{
    __html: `</script><link rel='preload' href='style.css' as='style' onload="this.onload=null;this.rel='stylesheet'"/><script>`,
  }}
/>

The Next.js team indicated that a similar strategy is possible with their component, but in practice, I was getting compilation errors: https://github.com/vercel/next.js/issues/8478#issuecomment-524332188

The error I received was:

Error: Can only set one of children or props.dangerouslySetInnerHTML.

like image 21
Colin Avatar answered Nov 12 '22 23:11

Colin