create-react-app (which I will sporadically refer to as CRA) is a great way to bootstrap a new site, and comes with a number of other benefits: config is hidden away in one dependency, leaving you to get on with the important stuff and taking a cognitive load off; you can update a project with the latest of all this config and tooling, in-place, in just a few seconds; and every project built with it has a familiar base, reducing the ramp-up time as you and your teammates switch projects.
All of this is great, but sometimes you need that extra bit of configurability - one jest parameter which isn't supported by CRA, an extra webpack loader, or a different ESLint rule set. The easiest way to do this is to run
yarn eject, unpacking all of what CRA hides away for you and dropping it in the project root for you to do with as you please. Unfortunately in doing this you lose the in-place updates with the latest tooling from Facebook, the configuration facade that lets you focus on development, and inevitably projects begin to diverge.
create-react-app repository is an example of a monorepo - it contains a number of packages, each its own module and individually published, but all having some involvement in
create-react-app. One of these packages is called
react-scripts, and within it is all of the configuration and default project files included when you create a new CRA project. By forking the repo and publishing our own version of the
react-scripts package we can take full control of these, without losing the benefits of using
If you use the forked scripts on a team, this also means that one person's improvements can benefit everybody - even those working on existing projects - and you'll still get all the new work from Facebook's end too.
First, you need your own copy of CRA. To fork the repo, open it on github and hit "Fork" in the top-right (sign in if you haven't already). This will create a copy of the repository under the same name but in your github account. Now clone your forked repo somewhere,
cd in to it and run
yarn. There'll be a little more output than usual here while it sets up dependencies for the individual packages within the repo.
Once the process completes, browse to
packages/react-scripts in your editor. You'll see a few key files and folders inside.
config- contains configuration for Jest, webpack builds and dev server
scripts- this scripts available for running in a
create-react-appproject such as
template - the files you actually see when you've run
create-react-app - the base of every new project
.gitignore- this is to avoid ignoring files from the template itself, but will be automatically renamed when generating a project
package.json- describes this package and its dependencies
This is where you can now customise the output of CRA to work better for you. Here are a few of our tweaks in the Amido fork, linked to the relevant commits in case you want to try something similar:
template\src\util- invaluable during development and preconfigured not to run in production, almost every project makes use of this. And if not, since it's in the
templateit's exposed and can be easily removed after project creation.
.eslintrc- this still includes
react-app's default rules, but adds Airbnb's rules on top for cleaner and more consistent code. As we figure out an optimal rule set (some of Airbnb's are a little overzealous for our taste) we drop changes in here, and will eventually bring it back to the hidden
configfolder when we're happy with the set.
Once you're happy with your changes it's time to test them out. You'll need to head up to the root of your repo and create a project using your local changes:
cd ../.. # head up two levels from `react-scripts` to the CRA root yarn create-react-app test-app
This command builds a local copy of
create-react-app, integrating your changes, then uses it to create a
If all goes well, you can now explore the resulting
test-app folder and try running the project locally:
cd test-app yarn start
When you're happy with your changes, you just need to publish them to npm. This makes them publicly available and ready for consumption by anybody using
create-react-app. If you don't have an npm account, first register and sign in by following the "Create a user" instructions.
Navigate back to the
First, we need to give the package a distinct name. Open up
package.json and replace the
name field with whatever you want to call this package (this is what people will reference to use with CRA). Make sure it's available by checking
npm.im/<PACKAGE-NAME>. For any future updates, note you'll also need to increment the version number.
Now, still in the
react-scripts directory, run
react-scripts is now ready to be used by you and your team, or anyone you wish to share it with! Try it out like so, replacing 'amido-react-scripts' with your package's name:
yarn global add create-react-app # if you haven't installed CRA globally already create-react-app --scripts-version=amido-react-scripts my-new-app cd my-new-app yarn start
If (as is hopefully the case) you find yourself using your custom scripts regularly, an alias can save time and make it easier to remember. In my
aliases.zsh I have
alias create-amido-app="create-react-app --scripts-version=amido-react-scripts"
The final thing you'll need to do is occasionally (or when particular features you want are added) pull in the latest upstream changes from
react-scripts. The first step is to add the Facebook repo as a new remote.
git remote add upstream https://github.com/facebookincubator/create-react-app.git git fetch --all
Now - and this is important - don't just pull in the latest from
upstream/master. Master doesn't necessarily match up with the latest published release of all packages, and the
--scripts-version flag only overrides the
react-scripts package. If the latest code relies on unpublished versions of other packages in the repo, you'll run in to problems after merging.
To merge published changes only, head over to the CRA Releases page and grab the commit hash from the latest release. At the time of writing, for example, that's
Now you can safely merge in the changes up to that release and publish your latest version.
git merge a51be95 # Fix any conflicts that might arise npm publish
That’s it! Hopefully you find this as convenient and time saving as I do. If I think of any similarly neat ideas, they might appear on my twitter @callummr alongside rants about the tube – feel free to follow!