React: Bake from Scratch or Box (JavaScript Version): Part 5
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:
- Adding a Stylesheet
- Adding CSS Modules
- Adding a Sass Stylesheet: Does require installing node-sass
- Post-Processing CSS: Configurable from browserslist key in package.json
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.