After working half a year with React and it’s ecosystem, I thought it was a good moment to analyze the things I’d like to take into account on future React projects.

These are mostly related with custom build configuration and improvements over the default configurations that we get while using popular tooling.

Create React App

react

On my first important project with React I decided to start off using the popular create-react-app module, which let’s us create react apps with no build configuration.

Soon I realized the project had some specific configuration needs (like integrating PIXI.js with the build process) so I decided to eject in order to have control over the project build.

At that moment, to integrate a library like PIXI.js we had to configure Webpack like the following gist:

Luckily, the PIXI community figured out how to import the library as a ES6 module in v4.1.1 so we don’t need to have a special configuration anymore.

Ejecting from a Create React App means that we won’t be able to upgrade react-scripts (the module that contains the build configuration) whenever the community releases a new version.

And this is really important in order to stay up to date with the latest releases of each dependency and build configuration updates, which believe me, we want to have in our projects since each release includes important bug fixes and improvements.

So, what’s the alternative to ejecting?

Custom react-scripts

Forking the create-react-app project itself, making the necessary changes to react-scripts and publishing a new npm package with these changes.

Sounds easy, right? :D

Fork create-react-app

Well, before even starting to implement this approach, we need to understand that the create-react-app Git repository is a monorepo.

That means it’s team decided to include most of the packages, built in favor of creating the create-react-app module, in the same Git repository.

We’ll find a packages/ directory in the repository which contains npm packages such as react-scripts (and create-react-app itself!).

Brainfuck

You can learn more about monorepos management at lerna.

So, in order to make changes to react-scripts and publishing to a new npm package, we first need to fork create-react-app.

My own fork, and specifically the custom react-scripts module, can be found at aaccurso-react-scripts.

Modify react-scripts

One of the most important things I wanted to change in the default Webpack configuration was to allow import of modules relative to the src/ directory.

Let’s say we are working in a component Foo located deep in the src/ directory structure and we want to use Bar which is way up in the directory hierarchy.

By configuring Webpack to search for modules relative to src/, we can get rid of the nasty ../../../../ in the import path.

You can find out how to do it in this commit.

There are other reasons for having to modify react-scripts, like supporting SASS (implemented in this commit) or integrating a library like PIXI.js.

I’d love to hear more examples in the comments section.

Publish to npm

Now that we have the changes in place, we need to publish them in a new npm package.

In order to do that, we’ll have to rename the packages/react-scripts/package.json package name, let’s say to my-react-scripts-fork.

Then we’ll npm publish and that’s it, we have a brand new npm package.

For instance, my custom aaccurso-react-scripts package can be found here.

The scope of this post doesn’t include topics such as release management of npm packages. However, you can find more information about this in the npm docs.

Upgrade react-scripts

To get notified of changes in the base repository of our fork we can use backstroke.

This tool will create and maintain a PR on our fork whenever there are new changes in the base repository.

Then we can decide how and when to include those changes in our fork, and by doing that we’d be upgrading to the latest version of the base react-scripts.

My React App

Now that we have our own custom my-react-scripts-fork, how do we use it in our project?

If it’s a project we already created, we just need to npm uninstall --save-dev react-scripts and then npm install --save-dev my-react-scripts-fork.

For new projects, we need to create-react-app my-react-app --scripts-version my-react-scripts-fork.

You can take a look at my-react-app which is using aaccurso-react-scripts.

Quite easy!

Local development

Now we want to make a change to my-react-scripts-fork and see the changes in my-react-app without having to publish a new version.

To do that, we’ll have to cd into my-react-scripts-fork directory and npm link the package.

Then we’ll cd into my-react-app and npm link my-react-scripts-fork.

This is very useful when we are testing a new configuration, but you have to remember to release and publish when you get the job done so the rest of your team can pull the changes.

Conclusion

We’ve seen how to extend react-scripts without ejecting and not “die” trying.

One thing I noticed while writing this post, is the fact that for most changes we need to duplicate the configuration in 2 webpack files: webpack.config.dev.js and webpack.config.prod.js.

I hope the create-react-app team (or even Webpack) comes up with a better approach to this in the future.

Which will mean more effort while merging our custom react-scripts to a new major version with breaking changes.

That’s why a complete Git commit history and changelog are important to help us in these matters.

I hope this helps you on your own projects.