Why I love Create React App

Create React App is a great React app starter and runner.

Before Create React App I always copy-paste webpack config and other *rc files from previous applications to each new application or even playground.

Create React App provides simple cli command to create application with the initial project structure, install dependencies and configure package.json scripts (run / build / test).

npm install -g create-react-app

create-react-app my-app
cd my-app/
npm start # yarn start

And there are no configuration. Developers can’t configure webpack, babel presets until ejection.

If you’re a power user and you aren’t happy with the default configuration, you can “eject” from the tool and use it as a boilerplate generator.
Running npm run eject copies all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. Commands like npm start and npm run build will still work, but they will point to the copied scripts so you can tweak them. At this point, you’re on your own.

Why I don’t want to eject my application

This is a one-way operation. Once you eject, you can’t go back! And I never run eject at my application because:

I want to receive create-react-app updates

For example Create React App team decided to switch to webpack 2 to support tree-shaking and a lot of other awesome features. It will be possible by just updated create-react-app version instead of updating my webpack configs.

Or Create React App team will release the Plugin System feature. I will be glad to use it and even create my own plugins. But if I had already ejected application — I will have to migrate back to Create React App.

I don’t want to see a lot of the dependencies at my package.json

Maybe this is not a problem but it is annoying me when I see all the babel*, eslint* and webpack* dependencies at package.json.

Extra configs and extra code

I always try to keep application as clean as possible. After ejection there are additional scripts and config directory . So after ejection there are ~10 new files with 50–200 lines of code at each file and in most cases only ~5 lines will be changed to add unsupported out of the box feature.

Back to the roots

After ejection and editing the configs I should copy-paste them to other applications because in most cases application’s configs (webpack loaders and plugins, babel presets) are the same.

I love Create React App because

Tool from React developers

No one can develop tool and configure bundler, code transpiler and test runner better then React developers :)

There are no configuration and no extra files at my app structure

As I wrote before — I always try to keep my application clean. So I am glad NOT to see webpack.config, scripts directory and additional dependencies at my package.json :)

Updating development stack with a single line change

Fixed development stack

Means that developers will use only stage-3 features (release candidates). Possible result of using draft features is:

Incredible fast developing tool

As the user of Create React App I am sure that I will receive all new stable features as fast as possible.

Also there are lot of interested feature requests from maintainers and community. For example:

Some of them are already closed or released but I just want to show that there is a huge flow of ideas. There are even third-party tools for Create React App ecosystem like CRA generate: Scaffold a React component.

My experience using Create React App

Because of Create React App we stopped to use CSS Modules, React Toolbox and some libs that should be at webpack externals.

By the way — react-toolbox-themr providing an easy integration with Create React App

And I still don’t want to eject!

Instead of CSS Modules I started to use glamor or aphrodite. There is the similar tool — Radium, but I don’t want to use style attribute for component styling.

Loading from the global folders

I can’t work with code that require modules from parent directories:

const dateUtils = require('../../../utils/date');

Usually with webpack this is resolved by resolve.modulesDirectories configuration. As we already know — there is no webpack configuration at Create React App application but there there two solutions.

Official solution: create node_modules at src directory - src/node_modules as official solution for absolute imports #1065.

Another way — use NODE_PATH env variable. It is a directory name to be resolved to the current directory as well as its ancestors, and searched for modules. More details at node official documentation "Loading from the global folders".

Use cross-env to set environment variables across platforms.

"scripts": {
"start": "cross-env NODE_PATH=src/scripts react-scripts start"
}

Or if you use .env config - just add NODE_PATH variable:

NODE_PATH=src/scripts

Environment configs

Create React App support .env out of the box (via dotenv). So just create .env at the root and run application. Make sure to add REACT_APP_ prefix to each variable. More info at official documentation — Adding Custom Environment Variables and Adding Development Environment Variables In .env

Multiple environment configs

Sometimes it is very useful to split configs by environments (api entry points, services keys and so on). For example my dev.env:

REACT_APP_GOOGLE_CLIENT_ID = XXX-YYY-ZZZ.apps.googleusercontent.com
REACT_APP_API_PROTOCOL = http:
REACT_APP_API_HOST = localhost:3000
NODE_PATH = src/scripts
PORT = 9001

And my prod.env:

REACT_APP_GOOGLE_CLIENT_ID = ZZZ-YYY-XXX.apps.googleusercontent.com
REACT_APP_API_PROTOCOL = https:
REACT_APP_API_HOST = api.my-applicaton.com
NODE_PATH = src/scripts

Right now it is possible by installing dotenv and updating npm scripts:

"scripts": {
"start": "node -r dotenv/config ./node_modules/.bin/react-scripts start dotenv_config_path=dev.env",
"build": "node -r dotenv/config ./node_modules/.bin/react-scripts build dotenv_config_path=prod.env"
}

Also there is the Create React App feature request with implemented Pull Request — Support different env configs.

Read different .env configs according to current command (start / test / build). dev.env for start and test. prod.env for build. If custom config does not exist — read env variables from .env file.

So if you like this feature — provide feedback at Issue thread.

Change the dev server port

I can’t run application at 3000 port because we use Google Auth and few years ago it was configured for development at localhost:9001–9005 ports.

Add PORT env variable. Use cross-env to set environment variables across platforms.

"scripts": {
"start": "cross-env PORT=9001 react-scripts start"
}

If you use .env config - just add PORT variable:

PORT=9001

Change webpack config — add plugins and change entry point

I have two situation when I have to do this:

  1. Add Webpack Offline Plugin.
  2. Publish single component as the separated build. (Seems related to How to publish components without ejecting #796)

At the first view seems that this is not possible without ejection. But there is the workaround. Anyway I don’t suggest to use this often. I am pretty sure that a lot of features are reachable with default Create React App configuration.

Always remember that using not usual loaders (like yaml, markdown, dsv loaders etc.), additional plugins and features from drafts and proposals makes your application more complex, maybe with dead syntax features and it is become impossible to migrate from current webpack configuration.

That is the reason I even do not like webpack css-loaders. I always try to require only js or json modules. From my point of view — css files are OK only at webpack entry point configuration but not for require function.

Add build:custom to npm-scripts:

"scripts": {
"build:custom": "node scripts/webpack"
}

And then create scripts/webpack.js:

const webpack = require('react-scripts/node_modules/webpack');
const craWebpackConfig = require('react-scripts/config/webpack.config.prod');
const OfflinePlugin = require('offline-plugin');

const config = {
...craWebpackConfig,
plugins: [
...craWebpackConfig.plugins,
new OfflinePlugin()
],
entry: [
craWebpackConfig.entry[0], // pollyfils
path.resolve('src', 'awesome-component.js')
],
output: {
...craWebpackConfig.output,
path: path.resolve('pf-build')
}
};
webpack(config).run(function(err, stats) {
if (err !== null) {
console.log('done');
} else {
console.log(err);
}
});
There is just webpack config extending, not react-scripts build. There are no beautiful console logs, comparison of the build sizes and other react-scripts build command features.

There is Create React App feature request — Feature request: export scripts (start/test/build) as functions that may help to use beautiful or more correct solution.

Custom babel presets and plugins

Create React App doesn’t support decorator syntax at the moment.

There are PR Adding support for custom babel configuration #1357. If PR is merged then these features will be available:

By default — no custom babelrc. Create it only if needed. But always remember… few paragraphs above.

Despite the fact that I’m the author of Pull Request — I agree with Dan Abramov.

The end

Awesome Create React App — A collection of awesome things regarding Create React App ecosystem. Feel free to file issue or suggest articles, videos and other useful resources via Pull Requests.