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.
ℹ️ 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]
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, which is an extension for Jest that exports test results in JUnit XML format, which can then be accepted by Continuous Integration (CI) tools such as CircleCI 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.
ℹ️ 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
:
{
- "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, 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.
ℹ️ 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:
{
"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].
Custom Template
The CRA team also added a 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, a Tailwind CSS template, and a template containing Storybook.
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 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]
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'spackage.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.
⚠️ 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, 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.
ℹ️ 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
👍 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
👎 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
👍 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)
👎 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.
[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. 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.
[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 for more information.
[3] https://create-react-app.dev/docs/custom-templates/#building-a-template
Top comments (0)