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.