Or how to heavily modify Create React App settings without having to eject.

I love using Create React App as a starting point for my React front-end as I get to concentrate on code and delivering value. Plus, by updating it I get new features without having to spend hours configuring them myself.
That is, until you want to do something custom with your app that the authors did not intend to. In that case, you have two choices, neither of them being great:

  • eject and expose all logic/dependencies behind react-scripts. This transforms projects into a monstrosity of build settings and dependencies. Then you have to live with it, update CRA yourself, etc because once ejected - there is no going back
  • fork create-react-app and use that as a dependency instead of in your project. You are still left with syncing all the later changes and improvements done on the CRA end. But at least your project is not polluted with everything that happens behind the scene

I wanted to include Stylelint into a project to lint CSS. Basic functionality can be achieved quite easily - yarn add stylelint --dev and add an npm script to your package.json. Something like this:

  "scripts": {
    ...
    "lint:client:css": "stylelint 'src/scss/**/*.scss'",
    ...
  },

This is all sweet and dandy, but it has certain limitations:

  • it does not watch for file changes
  • it is not integrated into Webpack so it will not flash a message in your browser and terminal like ESLint does in case there is a linting error

In order to have that last piece of the integration puzzle, you need stylelint-webpack-plugin. But that requires either of the two options from above, right? Well, there is another option I came across recently!

Meet react-app-rewired

This package sits between your local app configuration and CRA's config and overwrites CRA on the fly with any loader, plugin, or setting that can be overwritten. So you get all the benefits of CRA without the limitations of "no config". Pretty cool!

Anyway, back to Stylelint.

Let's install what we need first. Optionally you'd want stylelint-config-standard, depending on what kind of stylelint config you want.

yarn add react-app-rewired stylelint stylelint-webpack-plugin --dev

Then create config-overrides.js in your project root.

// config-overrides.js

const StylelintPlugin = require('stylelint-webpack-plugin')

module.exports = {
  webpack: function (config, env) {
    if (env === 'development') {
      config.plugins.push(
        new StylelintPlugin({
            // options here
        })
      )
    }

    return config
  }
  // jest: function(config) {
  //   // customize jest here
  //   return config;
  // },
  // devServer: function(configFunction) {
  //   return function(proxy, host) {
  //     // customize devServer config here
  //     return config;
  //   }
  // }
}

Now in your package.json, "start": "react-scripts start" becomes "start": "react-app-rewired start".

That's it. react-app-rewired will merge this setting with the settings on the CRA end. Your project remains clean and maintainable.

There are also default rewires that you could use, such as relay, eslint (in case you want a different config), etc.

In and out. Ciao!