Setting up a new React project used to be a huge challenge, as one needed to install so many dependencies, config files, and other setups before even a single line of code was written. This is where create-react-app, which is a tool released by Facebook, really helps. It can help in setting up a new React project within minutes. This tool wraps up all the common dependencies so that a developer does not have to worry about the configuration stuff and can, instead, just focus on developing React code itself. Let us have a look at using this tool.
With NodeJS/NPM installed on your machine, you can just run the following command:
1npm install -g create-react-app
It is recommended to install create-react-app globally so that it can be used at any location and for creating multiple React projects.
Now that we have create-react-app installed, we can create a new app by simply running the following command:
1create-react-app my-react-tutorial-app
2cd my-react-tutorial-app
The above will create a new directory, my-react-tutorial-app, and will contain the boilerplate for our application.
The project layout should look like this:
1├── README.md
2├── node_modules
3├── package.json
4├── .gitignore
5├── public
6│ ├── favicon.ico
7│ ├── index.html
8│ └── manifest.json
9└── src
10 ├── App.css
11 ├── App.js
12 ├── App.test.js
13 ├── index.css
14 ├── index.js
15 ├── logo.svg
16 └── serviceWorker.js
Above, we see the basic required files for our app. create-react-app just helps in creating a frontend build pipeline and is not really concerned about the backend. We can use any backend that we want for our created app. create-react-app also uses Babel and Webpack behind the scenes. But it is not essential to understand how Babel or Webpack work, for our needs here.
Let's have a look at our package.json which is generated by the command.
1{
2 "name": "my-react-tutorial-app",
3 "version": "0.1.0",
4 "private": true,
5 "dependencies": {
6 "react": "^16.5.2",
7 "react-dom": "^16.5.2",
8 },
9 "devDependencies": {
10 "react-scripts": "1.0.7"
11 },
12 "scripts": {
13 "start": "react-scripts start",
14 "build": "react-scripts build",
15 "test": "react-scripts test --env=jsdom",
16 "eject": "react-scripts eject"
17 }
18}
Here, we have the following attributes:
If we have a look inside node_modules, we'll see that it contains all the "dependencies" and "devDependencies" required by our React app. These are as specified or seen in our package.json file. If we just run the ls -l command, we'll see almost 700 - 800 sub-directories. This directory gets added to .gitignore, so it does not really get uploaded/published as such. Also, once we minify or compress our code for production, our basic tutorial app would not be more than 100 KB.
This folder has all the assets which will be served directly without requiring any kind of additional processing by Webpack. index.html is the entry point for our tutorial project/app. We can also see a favicon (or header icon) plus manifest.json.
This manifest file provides configuration for our web application and shows how the application will behave once it gets added to any mobile-user’s home screen.
Now that we have our folder, my-react-tutorial-app folder, created, let's go inside that using:
1cd my-react-tutorial-app
Then we can run the following commands:
1npm run build
This would build our application for production to the build directory. The "build" folder would contain all the static files which can be directly used on any web server. Also, the build command transpiles our source code into code which the browser can understand. It uses Babel for this and files are optimized for best performance. All of our JS files are bundled into a single minified file and even HTML/CSS code is minified to significantly reduce the download times on the client's browser.
1npm start
This would run our application in development mode. We can just navigate to http:localhost:3000 in any browser to preview our app live. The page will automatically reload whenever it detects any code change in the source files. Warnings and errors can also be seen in the console.
Internally, npm start uses webpack dev server to start a dev server so that we can communicate with the same.
1npm test
This command would run the tests in an interactive manner. The default configuration is to run tests which are related to the files updated since the last commit.
When a new app is created using the create-react-app, all of our build settings are actually preconfigured by the tool. Thus, we cannot make any updates to the build setup, e.g. we do not get access to webpack.config file. It is actually managed by the "react-scripts" build dependency. But there is a way by which we can customize the setup and not get restricted by the configuration provided by Create React App. To do that, we can just execute the command:
1npm run eject
Ejecting will give us complete control over the config files as well as dependencies like Webpack/Babel/ESLint. Ejecting actually forks the Create React app config and moves that into our app. After running the eject command, we can see a 'config' folder created in our project which has files like webpack.config for Development and Production plus a webpackDevServer.config file. Also, we can see that in package.json, the single build dependency react-scripts is removed from our tutorial project and all the individual dependencies are listed.
However, please be aware that running eject is an irreversible step or action. i.e. we cannot revert or go back to the initial state after running this command.
After we eject, we can continue working on our app normally and all the commands discussed above will work (build, start and test). But, we are now responsible for the configuration. So it is best to avoid using eject if we do not have much knowledge about configuration or dependencies. At least for the tutorial project, it is best to not use it.
For reference, this is how the package.json would look like after running the eject command:
1{
2 "name": "my-react-tutorial-app",
3 "version": "0.1.0",
4 "private": true,
5 "dependencies": {
6 "@babel/core": "7.1.0",
7 "@svgr/webpack": "2.4.1",
8 "babel-core": "7.0.0-bridge.0",
9 "babel-eslint": "9.0.0",
10 "babel-jest": "23.6.0",
11 "babel-loader": "8.0.4",
12 "babel-plugin-named-asset-import": "^0.2.2",
13 "babel-preset-react-app": "^5.0.2",
14 "bfj": "6.1.1",
15 "case-sensitive-paths-webpack-plugin": "2.1.2",
16 "chalk": "2.4.1",
17 "css-loader": "1.0.0",
18 "dotenv": "6.0.0",
19 "dotenv-expand": "4.2.0",
20 "eslint": "5.6.0",
21 "eslint-config-react-app": "^3.0.3",
22 "eslint-loader": "2.1.1",
23 "eslint-plugin-flowtype": "2.50.1",
24 "eslint-plugin-import": "2.14.0",
25 "eslint-plugin-jsx-a11y": "6.1.1",
26 "eslint-plugin-react": "7.11.1",
27 "file-loader": "2.0.0",
28 "fs-extra": "7.0.0",
29 "html-webpack-plugin": "4.0.0-alpha.2",
30 "identity-obj-proxy": "3.0.0",
31 "jest": "23.6.0",
32 "jest-pnp-resolver": "1.0.1",
33 "jest-resolve": "23.6.0",
34 "mini-css-extract-plugin": "0.4.3",
35 "optimize-css-assets-webpack-plugin": "5.0.1",
36 "pnp-webpack-plugin": "1.1.0",
37 "postcss-flexbugs-fixes": "4.1.0",
38 "postcss-loader": "3.0.0",
39 "postcss-preset-env": "6.0.6",
40 "postcss-safe-parser": "4.0.1",
41 "react": "^16.5.2",
42 "react-app-polyfill": "^0.1.3",
43 "react-dev-utils": "^6.0.3",
44 "react-dom": "^16.5.2",
45 "resolve": "1.8.1",
46 "sass-loader": "7.1.0",
47 "style-loader": "0.23.0",
48 "terser-webpack-plugin": "1.1.0",
49 "url-loader": "1.1.1",
50 "webpack": "4.19.1",
51 "webpack-dev-server": "3.1.9",
52 "webpack-manifest-plugin": "2.0.4",
53 "workbox-webpack-plugin": "3.6.2"
54 },
55 "scripts": {
56 "start": "node scripts/start.js",
57 "build": "node scripts/build.js",
58 "test": "node scripts/test.js"
59 },
60 "eslintConfig": {
61 "extends": "react-app"
62 },
63 "browserslist": [
64 ">0.2%",
65 "not dead",
66 "not ie <= 11",
67 "not op_mini all"
68 ],
69 "jest": {
70 "collectCoverageFrom": [
71 "src/**/*.{js,jsx}"
72 ],
73 "resolver": "jest-pnp-resolver",
74 "setupFiles": [
75 "react-app-polyfill/jsdom"
76 ],
77 "testMatch": [
78 "<rootDir>/src/**/__tests__/**/*.{js,jsx}",
79 "<rootDir>/src/**/?(*.)(spec|test).{js,jsx}"
80 ],
81 "testEnvironment": "jsdom",
82 "testURL": "http://localhost",
83 "transform": {
84 "^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest",
85 "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
86 "^(?!.*\\.(js|jsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
87 },
88 "transformIgnorePatterns": [
89 "[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$",
90 "^.+\\.module\\.(css|sass|scss)$"
91 ],
92 "moduleNameMapper": {
93 "^react-native$": "react-native-web",
94 "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
95 },
96 "moduleFileExtensions": [
97 "web.js",
98 "js",
99 "json",
100 "web.jsx",
101 "jsx",
102 "node"
103 ]
104 },
105 "babel": {
106 "presets": [
107 "react-app"
108 ]
109 }
110}
Jest is the default testing framework which is automatically configured when we use the Create React App. Jest can be used for writing unit tests for our individual components to verify that they behave as expected.
If we follow standard file naming convention like .spec.js or .test.js OR if all our test files are placed inside the tests folder, Jest will detect them and run our tests. Example: Inside the src directory of our React tutorial project, we can see the file App.test.js. It is a basic unit test setup by Create React App to test our App component.
Running "npm test" will launch our test runner in watch mode. Thus, every time we make updates to any test file, it would re-run our tests. This is exactly the same behavior as npm start, which recompiles our source code when any of our source files are updated.
As seen above, we now have a React tutorial project running on our machine without worrying about any dependencies or complex configurations. Also, we did not have to create our project structure manually.