Power up create-react-app!
Tags: react, javascript, typescript, node
Published: 2020-08-27 16:50:52

**create-react-app** (CRA) provides developers with the ability to quickly spin up single-page web applications (SPA) using the React framework without wasting time with configuration or version upgrades. It is a powerful toolkit that has helped make React a dominant player in the web framework space.

There are times however when the out-of-the-box configuration provided by CRA is not enough. Perhaps you want to install a new tool, or you're behind a company firewall and need to use audited dependency versions. In situations like these, CRA provides you with the option to "eject" from the CRA setup. This will allow you to have full control over all dependencies and run scripts. However, this will also prevent you from receiving new upgrades to the React toolchain from CRA. It will also make the React setup much more difficult to manage for newcomers to the framework.

If you want to have more control over your dependencies, but still receive upgrades and support to your React setup from CRA, there are two options available:

- **Forking the `react-scripts` package** from the official CRA repository, which is a core dependency of CRA applications that contains all the other dependencies. By forking this package, you can add your own dependencies in an encapsulated manner, and all projects using the latest version of your fork will get them automatically.
- Introduced in CRA v3.3.0, a **Custom Template** can be used to define a set of dependencies and scripts that can be added directly to a React project upon creation. (ie. direct dependency instead of through `react-scripts`) Templates provide the benefit of adding dependencies to your project transparently and allows you to update them independently of other projects that use the template.

In this post, I will walk through creating both a custom `react-scripts` fork and a custom CRA template, and I will compare both of the solutions.

-----------------

# Forking `react-scripts`

To get started with forking `react-scripts`, perform the following steps:

1\. Fork the [official create-react-app repository on GitHub](https://github.com/facebook/create-react-app).

| :information_source: Note |
| ------------- |
| You can also fork, or simply clone, the `packages/react-scripts` folder specifically if you like, since that's all we're touching for this tutorial. |

2\. Clone your newly forked repository to your local machine.

```
git clone https://github.com/<YOUR GITHUB USERNAME>/create-react-app.git
```
where `<YOUR GITHUB USERNAME>` is your GitHub username, assuming you have performed step 1.

3\. Checkout the latest release branch of CRA rather than from `master` branch to ensure stability. At the time of this writing, 3.4.1 is the latest release. [[1]](#1)

```
git checkout v3.4.1
```

4\. Now, navigate to the `react-scripts` package, in `packages/react-scripts`. Here is where the core CRA dependencies come from. By modifying this package, you will be changing what gets included in your React installation by default.

In my case, I wanted to add [jest-junit](https://www.npmjs.com/package/jest-junit), which is an extension for [Jest](https://jestjs.io/) that exports test results in [JUnit XML format](https://llg.cubic.org/docs/junit/), which can then be accepted by Continuous Integration (CI) tools such as [CircleCI](https://circleci.com/) to provide readable test results on every build.

I wanted this package to be included with all my current React projects, and every new one I make in the future. Thus, I installed it to the `react-scripts` package in my fork. This will make it available in all my React apps, so long as it's pointing to my fork of `react-scripts` instead of the official.

| :information_source: Note |
| ------------- |
| I also recommend making your changes in a new branch within your fork, so that when you pull in changes from upstream (ie. if CRA were to be updated) it's easy to merge in with your custom version. |

Once you finish making your changes, you will want to use your fork of `react-scripts` instead of Facebook's. To do this, you will need to make some changes to its `package.json`:

```diff
{
-  "name": "react-scripts",
+  "name": "<custom>-react-scripts",
  "version": "3.4.1",
-  "description": "Configuration and scripts for Create React App.",
+  "description": "Custom configuration and scripts for Create React App.",
  "repository": {
    "type": "git",
-    "url": "https://github.com/facebook/create-react-app.git",
+    "url": "https://github.com/<YOUR GITHUB USERNAME>/create-react-app.git",
    "directory": "packages/react-scripts"
  },
  "license": "MIT",
```

Change `<custom>` to something identifiable to you, and `<YOUR GITHUB USERNAME>` to your GitHub username.

You can test your custom `react-scripts` with a new React project by running:

```
npx create-react-app my-app --scripts-version file:../path/to/your/react-scripts
```

where `../path/to/your/react-scripts` can be either a relative or absolute path to your forked `react-scripts`.

The `--scripts-version` argument allows for a custom `react-scripts` to be installed in place of the official one. A name of an existing custom scripts from npm can be passed in, or a local copy can be passed in using the `file:` prefix, like we did above.

By making these changes, you will be able to publish it to the [npm registry](https://www.npmjs.com/), making it available for your React apps to install as a dependency.

To publish your `react-scripts` to npm, simply run `npm publish` and login with your npm credentials when prompted.

|:information_source: Note|
|-------------|
|Ensure the name of your custom `react-scripts` package isn't already taken on npm.|

Once your fork has been published, you can switch the dependency in your app like this:

```diff
 {
   "name": "my-app",
   "version": "0.1.0",
   "private": true,
   "dependencies": {
@@ -8,7 +8,7 @@
     "@testing-library/user-event": "^7.1.2",
     "react": "^16.13.1",
     "react-dom": "^16.13.1",
-    "react-scripts": "3.4.3"
+    "<custom>-react-scripts": "3.4.1"
   },
   "scripts": {
     "start": "react-scripts start",
```

`<custom>` is the identifiable name you gave to your forked `react-scripts` from the previous step.

You can also run `yarn remove react-scripts` then `yarn add <custom>-react-scripts` to install your fork.

Since the CRA team is continuously making new updates to `react-scripts`, you will need to keep your fork up-to-date as time goes on.

First, ensure your local repository is connected to the CRA team's version by adding an upstream remote, like this:

```
git remote add upstream https://github.com/facebook/create-react-app.git
```

Next, fetch upstream by running `git fetch upstream`

After that, apply changes from upstream to your local copy by running `git checkout upstream/vX.X.X`, where `X.X.X` is the newest version released, then switching into your custom branch and merging changes into it. `git merge vX.X.X`

There may be some conflicts, but should mostly just be simple version conflicts within `package.json`.

Also, to use your fork of `react-scripts` with new apps you make in the future, simply run:

```
npx create-react-app --scripts-version <custom>-react-scripts my-app
```

There is one slight caveat with this setup you will need to address manually if you also use a TypeScript template, see [[2]](#2).

-----------------

# Custom Template

The CRA team also added a [Custom Templates](https://create-react-app.dev/docs/custom-templates/) feature starting in v3.3.0, where you can simply have a template file containing your dependencies and scripts, and it'll add them to your project upon creation. This is an alternative to creating a custom fork of `react-scripts`, and it's useful when you only have a handful of dependencies and prefer to update them on a per-project basis.

There are many custom templates already published on the npm registry that you can plug-and-play, such as [this heavily customized Redux template](https://www.npmjs.com/package/cra-template-quickstart-redux), [a Tailwind CSS template](https://www.npmjs.com/package/@bamya/cra-template-tailwind), and [a template containing Storybook](https://www.npmjs.com/package/cra-template-storybk).

If you'd like to create your own template with your own set of dependencies and scripts, perform the following steps:

1\. Go to the official [create-react-app](https://github.com/facebook/create-react-app) repository and navigate to `packages` directory.

2\. Copy and paste one of the default templates as a base for your template. As of this writing, there are two official templates, `cra-template`, which is the default, and `cra-template-typescript`, which is the default TypeScript template.

3\. In `package.json`, add a new property called `main` and point it to `template.json`. At the time of this writing, this property is not present in the official templates and new projects will fail to be built if this property is not present in your template.

From the official webpage for Custom Templates, this is the directory structure for a template: [[3]](#3)

```
cra-template-[template-name]/
  README.md (for npm)
  template.json
  package.json
  template/
    README.md (for projects created from this template)
    gitignore
    public/
      index.html
    src/
      index.js (or index.tsx)
```

The important bits:

- `template.json` contains the dependencies, scripts, and other entries that will be copied over into the new React project's `package.json` file upon creation. They must be populated under a `"package"` field in this JSON file.
- `template/` directory contains files that will be copied over into the new project upon creation. `gitignore` will be renamed to `.gitignore`.

Update `template.json` with the dependencies you want to add to your project, add any files you will need to `template/` directory, and update `README.md` and `package.json` with information about your template.

|:warning: Important|
|-------------|
|All custom templates must start with `cra-template-` so that CRA knows it's a custom template. Ensure the name of your template within `package.json` follows this convention.|

Once all that's done, you can test your template by running:

```
npx create-react-app my-app --template file:../path/to/your/template/cra-template-[template-name]
```

where `../path/to/your/template/cra-template-[template-name]` can be either a relative or absolute path to your CRA template project.

Now you can publish the template to the [npm registry](https://www.npmjs.com/), making it available for new CRA apps to use as a template.

To publish your template to npm, simply run `npm publish` and login with your npm credentials when prompted.

|:information_source: Note|
|-------------|
|Ensure the name of your custom template package isn't already taken on npm.|

You can create new React projects using your template by running:

```
npx create-react-app my-app --template cra-template-[YOUR TEMPLATE]
```

-----------------

# Comparison

In this section, I will compare each of these two solutions. You may want to use one or the other depending on your situation, and you can also use both of them together!

## Forking react-scripts

### :+1: Benefits
- Allows you to update dependencies and scripts for your projects in one go
- Less dependency overhead in your projects' `package.json`
- Useful for managing dependencies if behind company firewall and/or using a corporate npm registry

### :-1: Downsides
- Not well suited for React projects that would need only a subset of the dependencies updated while keeping old versions of other dependencies (would need to start overriding dependency versions in `package.json` at this point)

## Creating templates

### :+1: Benefits
- Much simpler to use - simply specify the dependencies and scripts you need in the `template.json` file
- Inserts dependencies directly into your app upon creation, sidestepping the need to fork `react-scripts` if you want to manage the dependencies on a per-project basis
- Makes your dependencies visible, unlike the forked `react-scripts`, which encapsulates them (depending on the situation, this may be a pro or a con)

### :-1: Downsides
- Will need to update dependencies and scripts for every new project you make manually


-----------------

And that's it - You now have the ability to customize your CRA installation however you see fit! Let me know in the comments if there's something I missed, and heart and save it if you found this useful.

-----------------

<a name="1"></a>[1] The latest version of this writing is actually v3.4.3, but there were no commits between v3.4.1 and this version. The update was simply to bump up dependencies of some internal tools to satisfy audit requirements. You can learn more about this [here](https://github.com/facebook/create-react-app/discussions/9484). Because this minor change does not affect CRA itself, the maintainers felt there was no need to make a release entry for it on GitHub. Thus, v3.4.1 remains as the latest version for the purposes of this article.

<a name="2"></a>[2] When creating a new project using a TypeScript template, there is a special file called `react-app-env.d.ts` that allows special objects such as images and CSS modules to be detected by TypeScript. It does this by referencing a file in `react-scripts` that provides these type definitions. This reference to `react-scripts` does not change even if a custom `react-scripts` is substituted in place of the official `react-scripts`. At the moment, a workaround is to change the reference in `react-app-env.d.ts` to the name of your custom `react-scripts`. See this [issue](https://github.com/facebook/create-react-app/issues/8223) for more information.

<a name="3"></a>[3] https://create-react-app.dev/docs/custom-templates/#building-a-template