bene : studio is a global consultancy, helping startups, enterprises and HealthTech companies to have better product

Using Create React App for Generating Custom Boilerplates

boilerplate4

At bene : studio we love knowledge sharing to help the community of professionals. With 10+ years and over 100 projects behind us, we have a vast amount of experience. This is why we have launched a knowledge base on our blog with regular updates of tutorials, best practices, and open source solutions.

These materials come from our internal workshops with our team of developers and engineers.

Our workshops are restarting!

Join the FREE ONLINE React Native workshop on August 11, and learn how to connect native modules to build an awesome Music Creator.

Join the workshop and invite your peers as well! For full details and registration visit the event page:

Introduction

Create React App (CRA) is a great tool for quickly setting up a React application with a full-fledged development environment without knowing or worrying about the underlying building blocks. While it boasts itself as a no-configuration-required tool, this does not mean we have no control over the generated application. What makes this possible is the –template and –scripts-version options of the create-react-app command which allows the usage of custom templates and react-scripts versions, respectively.

In this article, I aim to explore the flexibility of this customizability by extending the React project that’s generated by default into a monorepo.

Ways of Customization

The ways we can manipulate the project generation process are through custom templates and react-scripts versions.

Surface level: CRA Templates

A CRA template is used to define two things: the initial project structure, i.e., the folders and files that the project will contain, and the contents of the generated projects package.json file, i.e., dependencies, NPM scripts, and so on.

Full control: React-scripts

The react-scripts package is an integral part of CRA. This package, alongside the configuration of the development environment, also houses the bulk of the code that automates the generation of the application. Modifying it allows for full control over this process. 

Creating a Custom CRA Template

The Create React App website illustrates the structure of a typical template like this:

A typical CRA template

The main parts are the template folder and the template.json file. The template folder contains the intended file structure. The content gets copied to the root of the application. The template.json serves as a sort of configuration file. By default, the only configuration it supports is under the “package” key. Dependencies and scripts defined under this key will be merged with the package.json file of the generated application.

The default template.json

There is also a list of blacklisted keys that will be ignored. Anything else will be just copied over the existing values.

Blacklisted keys under the “package” key

To create your own template, you can use the default one included in CRA as a basis. If you intend to use it as a Node package, name it using the following convention: cra-template-[template-name] or @[scope-name]/cra-template-[template-name]. This way you will be able to refer to it as [template-name] or @[scope-name]/[template-name].

Modifying react-scripts

Modifying an integral part of CRA might feel wrong, but forking and maintaining a custom version of it is actually something the original developers encourage us to do! Developers kept asking for a way to tweak the tools inside, and this was their answer. But it allows for more than just reconfiguring the development environment. Let’s see how to do it.

Getting the code

Create React App is maintained as a monorepo. Since react-scripts is a submodule of this repo, we cannot fork it the usual way. Fortunately, there is a Github article about solving this exact use-case: Splitting a subfolder out into a new repository.

In short:

  • Clone create-react-app.
  • Navigate to its folder and run git filter-branch –prune-empty –subdirectory-filter packages/react-scripts master.
  • Now the folder only contains the react-scripts module with the git history preserved.

A little aside on filter-branch: There is a better alternative to filter-branch called git-filter-repo. It’s faster, safer and even the git manual recommends using it. The only compromise to it is that it needs to be installed.

Modifying the automation

The whole setup automation, except for a minor part, can be found under scripts/init.js in one big function. It’s easily identifiable because it’s the only thing this file exports. I recommend reading through it if you are serious about modifying it. The code itself is pretty straightforward. 

In short, here are the key steps init.js executes:

  • Copies the file structure from the supplied template.
  • Merges the “package” object from template.json with the generated app’s package.json.
  • Installs dependencies.
  • Initializes a git repository.

In this file, we are free to add whatever custom code we want with the full power of JS and NodeJs backing us.

Maintaining the fork

Since one of the main advantages of using CRA is the guarantee of an up-to-date development environment, integrating the changes over time from the original repo is important. The easiest way I found is this: clone the original repo, split out the react-scripts folder just like the first time, and merge this repo into the customized one.

Creating the Monorepo Boilerplate

Now that we know all of this let’s build something with it! As a demonstration, I have created a monorepo boilerplate. This monorepo will have a React client and an Express server, and the repos orchestration will be done by Lerna.

The template

To create the monorepo, I built the following template.

My monorepo template

You can see that I have separated the individual packages of the monorepo into their respective client and server folders.

To feed some more information to react-scripts, I have extended the template.json with additional keys.

rootDependencies contains the dependencies for the monorepo orchestration.

packages contain information about the individual packages in the monorepo.

Bootstrapping with react-scripts

Okay, we have our scaffolding. Let’s go on to bootstrapping. For simplicity’s sake I have added my code at the end of the “main” function of init.js. Because of this, the first step is a little bit of cleaning up. he original init.js code does not know that the React applications location has been moved into a separate folder and because of this it places a few things in the wrong place. It creates a package.json file for the client which needs to be moved to the correct place. Also, it installs its dependencies in the root folder which I will just delete alongside the package-lock/yarn.lock file.

The next step is bootstrapping the packages based on the custom template.json.

And lastly, setting up the orchestration.

As you can see the code is very verbose, but also very simple. The variables that you don’t see being declared in these snippets are from the original script.

Both the template and the react-scripts are available on Github:

https://github.com/benestudio/cra-template-monorepo

https://github.com/benestudio/react-scripts-monorepo

Usage

Okay, we have created a custom template and we have our own fork of react-scripts. Let’s put them together! At the time of writing this article, I recommend only using it as a hosted package. I have run into trouble trying to use it from my local file system. I have published both the template and the react-scripts modifications to npm

To try it out, run:

After it completes, navigate to the project root and run npm start as usual. This screen should greet you.

Conclusion

What I learned is that CRA is a very flexible tool for a very specific purpose. That purpose is to create guaranteed up-to-date and functioning React applications. This guarantee combined with the customization options is a huge advantage. The downside is that storing the boilerplate code in this format is not very intuitive. Effectively modifying and maintaining it requires some ramp-up from the developers’ part, and as always outside toolings can cause all sorts of issues. Regardless, it could be a handy tool next to git-hosted repositories for storing boilerplates and I hope that at the very least you have gained more insight into the inner workings of CRA.

Want to ask some questions? Send them to partner@benestudio.co and we are happy to set up a talk with our engineers.

Looking for a partner for your next project? Check out our services page to see what we do and let’s set up a free consultation.

Join Our Engineering Team, We Are Hiring!

boilerplate4