Remove render-blocking styles from server side rendered React application
Asked Answered
U

0

6

I ran an audit of my React application using the chrome Lighthouse tool and it detected that my application had some render blocking css files. I investigated further by running a coverage and noticed that my application has about 50% of unused code (CSS & JS).

I've tried various plugins like: Critical, Penthouse, webpack-critical, webpack-plugin-critical, html-critical-webpack-plugin react-snap, to remove the critical css from the application but none have worked mostly because all the plugins that should help expect that the application makes use of HTMLExtractPlugin. However, this doesn't apply to me because my entire application is rendered on the server and served to the client.

Here is render function on my server:

const serverRenderer = () => (req, res) => {
    const content = renderToString(
        <Provider store={req.store}>
            <App />
        </Provider>
    );

    const state = JSON.stringify(req.store.getState());

    return res.send(
        `<!doctype html>
            ${renderToString(
                <Html
                    css={[res.locals.assetPath('bundle.css'), res.locals.assetPath('vendor.css')]}
                    scripts={[res.locals.assetPath('bundle.js'), res.locals.assetPath('vendor.js')]}
                    state={state}
                >
                    {content}
                </Html>
            )}`
    );
};

export default serverRenderer;

HTML COMPONENT

// @flow
/* eslint-disable react/no-danger */
import React from 'react';
import Helmet from 'react-helmet';

export default class HTML extends React.Component {
    static defaultProps = {
        css: [],
        scripts: [],
        state: '{}',
    };

    render() {
        const head = Helmet.renderStatic();
        const { children, scripts, css, state } = this.props;
        return (
            <html lang="">
                <head>
                    <meta charSet="utf-8" />
                    <meta name="viewport" content="width=device-width, initial-scale=1" />
                    {head.base.toComponent()}
                    {head.title.toComponent()}
                    {head.meta.toComponent()}
                    {head.link.toComponent()}
                    {head.script.toComponent()}
                    {css.map((href) => {
                        return <link key={href} href={href} rel="stylesheet" as="style" />;
                    })}
                    <script
                        dangerouslySetInnerHTML={{
                            __html: `window.__PRELOADED_STATE__ = ${state}`,
                        }}
                    />
                </head>
                <body>
                    <div id="app" dangerouslySetInnerHTML={{ __html: children }} />
                    {scripts.map((src) => {
                        return <script key={src} src={src} async />;
                    })}
                </body>
            </html>
        );
    }
}

Webpack config

const webpack = require('webpack');
const ManifestPlugin = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const Critters = require('critters-webpack-plugin');
// const HtmlWebpackPlugin = require('html-webpack-plugin');
/* const LoadablePlugin = require('@loadable/webpack-plugin');
const ChunkExtractor = require('@loadable/server');
const statsFile = path.resolve('../../rafikibuild/client/statics/loadable-stats.json'); */

const env = require('../env')();

const shared = [];

const client = [
    // TODO: add client side only mode
    // new HtmlWebpackPlugin({
    //     inject: true,
    //     template: paths.appHtml,
    // }),
    new CaseSensitivePathsPlugin(),
    new webpack.DefinePlugin(env.stringified),
    new webpack.DefinePlugin({
        __SERVER__: 'false',
        __BROWSER__: 'true',
    }),
    new MiniCssExtractPlugin({
        filename:
            process.env.NODE_ENV === 'development' ? '[name].css' : '[name].[contenthash].css',
        chunkFilename:
            process.env.NODE_ENV === 'development' ? '[id].css' : '[id].[contenthash].css',
    }),
    new Critters({
        preload: 'swap',
        preloadFonts: true,
    }),
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
    new ManifestPlugin({ fileName: 'manifest.json' }),
    new CompressionPlugin({
        filename: '[path].gz[query]',
        algorithm: 'gzip',
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
    }),
];

const server = [
    new webpack.DefinePlugin({
        __SERVER__: 'true',
        __BROWSER__: 'false',
    }),
];

module.exports = {
    shared,
    client,
    server,
};

Is there a way to remove render blocking styles on a server side rendered React application?

Thank you.

Unbound answered 24/9, 2019 at 11:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.