React: Bake from Scratch or Box (JavaScript Version): Part 5

John Tucker
codeburst
Published in
4 min readNov 30, 2018

--

Continuing side-by-side comparison considering styling.

This is part of a series starting with React: Bake from Scratch or Box (JavaScript Version): Part 1; a side-by-side comparison of creating React applications using a custom-built build solution (from scratch) versus using Create React App (from box).

Round 6: Styling

Might be giving away the punchline, but today any reasonable React build solution needs to support:

  • CSS (extension .css): Global CSS namespace
  • CSS Modules (extension .module.css): Local CSS namespace
  • CSS Preprocessor; SASS (extensions .scss, .sass, .module.scss, or .module.sass): Enabling variables with CSS among other features
  • CSS Postprocessing; Autoprefixer: Adding in vendor-specific CSS prefixes
  • CSS extraction: Pulling CSS out of JavaScript bundles and into CSS files

Round 6: Styling — Scratch

Let us implement these feature in our custom-build build solution:

note: The final version (of this round) of the custom-built build solution is available for download from the styling branch of the webpack-scratch-box repository.

We first add support for CSS and CSS Modules:

npm install --save-dev style-loader
npm install --save-dev css-loader

and update webpack.config.js:

...
const config = env => ({
...
module: {
rules: [
...
{
test: /\.css$/,
exclude: /\.module\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.module\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
],
},
...
});
...

Next we add support for SASS:

npm install --save-dev node-sass
npm install --save-dev sass-loader

and update webpack.config.js:

...
const config = env => ({
...
module: {
rules: [
...
{
test: /\.(css|scss|sass)$/,
exclude: /\.module\.(css|scss|sass)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
},
},
'sass-loader',
],
},
{
test: /\.module\.(css|scss|sass)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
modules: true,
},
},
'sass-loader',
],
},
],
},
...
});
...

Next we add support for Autoprefixer:

The first observation is that we are already using a browserlist key in package.json (from our Babel configuration).

We install the dependencies:

npm install --save-dev postcss-loader
npm install --save-dev autoprefixer

We create the Post CSS Loader configuration (postcss.config.js):

and update webpack.config.js:

...
const config = env => ({
...
module: {
rules: [
...
{
test: /\.(css|scss|sass)$/,
exclude: /\.module\.(css|scss|sass)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
{
test: /\.module\.(css|scss|sass)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
modules: true,
},
},
'postcss-loader',
'sass-loader',
],
},
],
},
...
});
...

We, finally, install the dependencies to extract the CSS:

npm install --save-dev mini-css-extract-plugin

and update webpack.config.js; conditionally extracting the CSS on production builds:

...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
...
const config = env => ({
...
module: {
rules: [
...
{
test: /\.(css|scss|sass)$/,
exclude: /\.module\.(css|scss|sass)$/,
use: [
env && env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
...
],
},
{
test: /\.module\.(css|scss|sass)$/,
use: [
env && env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
...
],
},
],
},
...
plugins: [
...
env && env.NODE_ENV === 'production'
? new MiniCssExtractPlugin({ chunkFilename: '[id].css', filename: '[name].css' })
: new NothingPlugin(),
],
});
...

And we update package.json to define a production build:

{
...
"scripts": {
...
"build-prod": "webpack -p --mode production --env.NODE_ENV=production",
...
"analyze": "webpack -p --mode production --env.analyze --env.NODE_ENV=production"
},
...
}

Round 6: Styling — Box

While this was not the case when I first tried Create React App (was the deal breaker for me), it now supports all the expected CSS features with minimal configuration:

Round 6: Styling — Comparison

While this was a reason to not use Create React App awhile ago, it now fully supports most of the key styling features.

At the same time, one challenge is using a library that depends on the Less preprocessor, e.g., Ant Design. Luckily they is a way to monkey-patch Create React App to support Less; a little bit fragile but at least it works.

Next Steps

In the next article, React: Bake from Scratch or Box (JavaScript Version): Part 6, we continue our side-by-side comparison by looking at optimization features.

--

--