Changing default CSS compression settings in Gatsby

A brief note on how to adjust Gatsby’s default webpack config to apply custom cssnano optimizations

What’s that?

Gatsby is an open-source, React-based, static site generator. It combines modern JS development environment, rich React ecosystem, and high performance into enjoyable developer experience. I use Gatsby in two projects, and I am happy with it.

Problem with default settings

Gatsby lets you create fast sites out-of-the-box by automatically applying various performance optimizations and improvements under the hood. While most of the optimizations work well, some of them might require additional tweaking.

We’ll touch upon CSS compression. It takes place during bundle compilation in webpack - one of Gatsby’s core packages. Handled by optimize-css-assets-webpack-plugin’s default CSS processor - cssnano, it occurs only for a production build.

During development, your site will work as expected, but once you switch to a production environment, it might behave unexpectedly:

Notice how the main heading is not changing its size when resizing the screen on production. The cause of it lies in the default cssnano config, which has buggy calc optimization turned on by default.

As a workaround, one can disable the calc() optimization. But how can we achieve this in Gatsby?

Custom webpack config to the rescue

Luckily there is a Node API called onCreateWebpackConfig, allowing us to change the existing configuration. Gatsby does multiple webpack builds with different configs; they call these stages. We need to target build-javascript stage - that’s where production bundles are being built. Inspecting Gatsby’s webpack.config.js reveals that the CSS optimization plugin is located inside optimization.minimizer array. Now we are ready to alter default cssnano options.

Here is what you need to add to your gatsby-node.js:

js
exports.onCreateWebpackConfig = ({
actions,
stage,
plugins,
getConfig
}) => {
// override config only during
// production JS & CSS build
if (stage === 'build-javascript') {
// get current webpack config
const config = getConfig()
// our new cssnano options
// are still based on default preset
const options = {
cssProcessorPluginOptions: {
preset: ['default',
{
discardComments: {
removeAll: true
},
calc: false,
reduceTransforms: false,
minifySelectors: false
}]
}
}
// find CSS minimizer
const minifyCssIndex = config.optimization.minimizer.findIndex(
minimizer => minimizer.constructor.name ===
'OptimizeCssAssetsWebpackPlugin'
)
// if found, overwrite existing CSS minimizer with the new one
if (minifyCssIndex > -1) {
config.optimization.minimizer[minifyCssIndex] =
plugins.minifyCss(options)
}
// replace webpack config with the modified object
actions.replaceWebpackConfig(config)
}
}

Besides disabling calc, I’ve also switched off minifySelectors and reduceTransforms as they both were messing up my CSS in the past, but your mileage may vary. Now we can create new production build and test it out.

And Voilà!

Our CSS behaves as expected again.

next

Designing and implementing dark mode on a website

© 2020 Eduard Tihhonov.