Log In

create-react-app


by Yariv Katz

Bootstrapping a react web application is hard work. There are many tools that we need to install and configure to help us on our way to create our web application. These are part of the questions our working environment will have to deal with:

  • We need to write unit testing to test our react components
  • How do we deal with images? we need to compress them before including them in our app bundle.
  • We want to use SCSS and compile it to CSS
  • Maybe we want to use LINT to verify our code standarts

Installing all the tools needed for a professional environment can be tedious especially if we have to do it again and again between every project. Facebook released a cli tool called create-react-app that helps us start a new react web project. In this lesson we will learn about create-react-app how to bootstrap a new app and the tools that this cli is installing for us.

Starting a new project

Let's start with creating a new react project. In your workspace we will start a new project by typing in the terminal:

> npx create-react-app cra-tutorial
> cd cra-tutorial

Let's start by examining the files created for us.

Project files

public in this folder we will place files that will simply be copied to the final bundle. Among the files here you will find the index.html file. This file will be copied as well only it will get minified and our app js scripts will be appended to the end of the body along with the css files will be at the head section. Usually we won't modify the index.html file and when we do it would usually be to place cdn js scripts or cdn css. A common mistake is to place the image files in that folder and that is not the place for image or font files. We would usually leave this folder as is.

src Our react project files will be located in this folder. create-react-app (CRA) already created us a root component that will wrap our react application. That component is in the file App.js. React also made some css files. let's go over them next.

Style files

CRA created the following style files for us. index.css the global app styles will be here. App.css the styles for the root app component will be in this file. To include a styling file in our app we need to simply import it. For example let's open the file index.js which is the entry point file for our app. You will notice at the top of the file:

import './index.css';

Same goes for the App.js you will notice at the top of the file:

import './App.css';

A better tool to use for styling is scss. Let's change the file extensions to scss and the imports in the js files to import those scss. The environment is already configured to deal with scss, we only need to install the node-sass package so in the terminal type:

> npm install node-sass --save-dev

So the practice here is that for every react component we create we create a style file for that component and import that style file at the top of the component. Notice that importing a style file like we did in the App component, does not mean that our styles will be encapsulated and effect only the app component.

image files

To inlcude images in our components we simply need to import them. Notice at the top of the App.js file:

import logo from './logo.svg';

And then to use that image you simply include it in the src of the image.

<img src={logo} className="App-logo" alt="logo" />

So image files that components are using will be inside the folder of that component and not in the public directory. This means that for each component with multiple files we will make a directory for the component and in that directory inlclude the styling file for the component, images that the commponent is using, test file for the component. Let's see a scenario where the image file is part of the styles. We want to include a background image for our app. In the src folder create a folders named styles. In the styles folder create a folder named scss that will contain all the scss files that compose the global styles (note that we will still place component styles in the component folder, global app styles will be in this folder). copy the index.scss to that folder and make sure to fix the import path in index.js. In the styles folder create another folder named images. In this folder we will place global app images and not component images that will still reside in the component folder. Copy a image of your choosing to that folder and name that image: bg.png. We want to include this image as a background for our application. Add the following to the end of index.scss

.App-header {
    background-image: url(../images/bg.png);
}

So you will not have to place all the images in the public folder rather distinguish between global images that will be in the styles/images folder and component images that will be in the component folder. Similar with font files, font definitions are global for our app so they should be placed in the styles folder we created. In the styles folder create a new folder fonts and download a free font from google font and cpy the fonts to the fonts directory we created. I will assume the font file name is font.ttf, at the end of the file index.scss add the following:

@font-face {
    font-family: 'stam';
    font-style: normal;
    font-weight: 400;
    src: url(../fonts/font.ttf);
}

h1 {
    font-family: 'stam';
}

So you can keep the font files and images in the styles directory and keep those files there instead in the public folder. Let's talk about best practices regarding arranging the files in your project.

How to arrange folder and files

We mentioned that our js react files will be in the src folder, but how do we arrange our files and folders in the src folder. We also mentioned that we will create styles folder and place global app styles related files in that folder. There are 2 common practices to arrange our file. The first one is suited more for smaller apps. We will arrange our files according to their type, for example: - react component files will reside inside a components folder and in the components folder there will be a folder for every component that contains the js of the component, test file of the component, styles related to the component and images.
- services - we will create a services folder with all the services of our app.
- models - Models folder will have the classes of our model building blocks of the app.

The only problem with this approach is that when the app grows larger, our app will contain a lot of react components and services and models and it will become hard to navigate through our file. So in a larger application I would recommend arranging the files based on application features. For example let's say my app contains a settings section with 20 different settings pages, I would probably want a settings folder in the src and in that folder arrange like the previous example with folders like components, models, etc. It is often useful for me to look at the urls of the application to determine the features of the site and how to arrange the feature folders. For example is I have the following urls:
- /dashboard/team
- /dashboard/statistics
- /dashboard/account
I would probably have a dashboard folder in the src with those items related to the dashboard pages/

Testing

React also create a file called App.test.js containing unit test for our App component. In fact we can just create a file with test.js extension and the environment is configured to run this file with the test environment. CRA configured Jest to run our tests. Jest is a browserless testing which can run unit testing without launching a browser, it will just mimic the browser environment and mimic a DOM. To run the tests you can simply type in the terminal:

> npm test

Personally I'm not a big fan of jest, and it seems like facebook is pushing forward a technology that is not as good as mocha. So let's try and config our environment to work with mocha. I also did not want to run my tests in a browserless environment since I wanted to debug my tests in a browser, so I want to use karma as well. So Lets try and configure a react app created with CRA to use Karma + Mocha + Enzyme for testing.

Now we need to install mocha and karma and we will also use chai as our assertion library. so in the terminal type:

> npm install mocha karma chai -D

Init karma with the following command:

> karma init

You will be asked a few questions:
- choose mocha as the testing framework.
- We will not use require.js
- Do you want to capture any browsers automatically ? Chrome
- What is the location of your source and test files ? test.js
- Should any of the files included by the previous patterns be excluded ? leave blank
- Do you want Karma to watch all the files and run the tests on change ? yes
The result is that a config file karma.conf.js is created for us. we will modify this file soon. Running the command:

> karma start

will run the file test.js which we did not yet create. For our tests, we will use enzyme to mount our component. We will add a simple test to the App.js component that mounts the component and verifys that there is an img tag after we mount the component. Enzyme is a react testing library that airbnb created that we can use to mount components and test events on those components. First lets install enzyme. In the terminal type:

> npm i --save-dev enzyme enzyme-adapter-react-16

On the installation instruction of enzyme we need to configure enzyme with the proper adapter depending on which version of react we are using. since we are using version 16 we need to use enzyme-adapter-react-16. Karma is configured now to run a single file: test.js
The job of that file will be to configure our test environment and then start launching our test files. Create that file and place the following in that file:

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

// configure enzyme for react 16
Enzyme.configure({ adapter: new Adapter() });

// find the tests and load them
const context = require.context('./src', true, /\.spec\.js$/);
context.keys().map(context);

So what we did here is configure enzyme as well as look for all the spec files and run them. To make things work on the browser we will have to make karma work with webpack. We will have to use the same webpack configuration that create-react-app is using. One option will be to run npm run eject that will make cra spill the webpack configuration file which you can then use. The only problem is that this is irreversible so after ejecting you will no longer be able to benefit from CRA. We want to use the same configuration with the need for ejecting. The configuration that cra is using is located at node_modules/react-scripts/config/webpack.config.js. So lets use that as a configuration file. We will need to install karma-webpack

> npm install karma-webpack -D

Now lets modify the karma configuration to use webpack. In karma.config.js edit the preprocessors section:

preprocessors: {
    "test.js": ["webpack"]
},

This means our test.js file will be compiled with webpack before running. In the configuration we need to include webpack configuration, so in the same file add the following at the top of the file:

function createWebpackConfig() {
    const craWebpackConfig = require('react-scripts/config/webpack.config.js')();
    delete craWebpackConfig['output'];
    delete craWebpackConfig['entry'];
    delete craWebpackConfig['optimization'];
    delete craWebpackConfig['plugins'];
    craWebpackConfig['devtool'] = 'inline-source-map';
    craWebpackConfig['mode'] = 'development';
    return craWebpackConfig;
}

What this function does is grab the webpack configuration from cra and modifies it a bit for our karma. Now at the same file at the karma configuration section, add the following:

webpack: createWebpackConfig()

So we are using the react-scripts webpack config, but note that you can also override it and add devtools sourcemaps or whatever configuration will fit you for running and debugging your tests, you simply need to configure the function createWebpackConfig

In our package.json let's modify the test command to launch karma.

"test": "export NODE_ENV=development && karma start",

You can now run the tests by typing:

> npm test

But we still need to create our first test using enzyme. Reminder we want to the App.js that when our component mounts there is an img tag. In the src folder add a new file: App.spec.js with the following:

import React from 'react';
import App from './App';
import { shallow } from 'enzyme';
import * as chai from 'chai';


describe('Testing App component', function() {
    let app;

    beforeEach(function() {
        app = shallow(<App />);
    })

    it('should render the component with img tag', function() {
        chai.expect(app.find('img')).to.have.lengthOf(1);
    });
});

This test uses enzyme shallow rendering which means it will only render our component without the children. Then we make sure we have an image tag.

So although we are in a closed to config cra environment we still managed to run our tests in a custom Karma + Mocha + Sinon + Enzyme. Lets go over the engine behind our configured environment.

webpack

We saw earlier that to include a stylesheet we need to simply import it. Rather if it is a css or a scss. How can we import a css or scss file in our js files. The import statements are interperted by a tool called webpack. Webpack is a web bundling tool, we configure webpack to deal with our project resources. So webpack can deal with images and compress them. Webpack can compile our scss files to css and minify them. Webpack can compile our typescript files and so on. Webpack is already configured for us by CRA but we can always custom configure webpack by running unreversable eject command:

> npm run eject

When ejecting you will notice that a new folder called config is created for you with the configuration for webpack in the file webpack.config.js Now you can modify the config file and add custom steps of your own. To build the project you run the command.

> npm run build

but after we ejected the webpack configuration we can no longer use the regular react-scripts build, rather there is a scripts folder for build, test and start the development server. before we can use that script we need to install a missing babel plugin.

> npm install babel-plugin-named-asset-import--save-dev
> node scripts/build.js

This will build your project after ejecting the config. Before ejecting the serve command was:

> npm start

After ejecting to run the development server:

> node scripts/start.js

So you can tell that ejecting added a scripts folder and to run build, serve or the tests we use the scripts from that folder.

To summerise what webpack is doing: webpack is looking at our import or require statements, and looks at the config and then it will know how to deal with that resource. That is what allowed us to import a scss file. This means that we include the styles of the components and images that the components use in the folder of the component.

Summary

To summerise. create-react-app is used to bootstrap a react web application. Behind the scenes it configured webpack for us so to include resources in our bundle we simply need to import them. There is a lot of angry users on CRA regarding the test framework and I have to say that I am one of them, CRA is suppose to be an unopinionated tool yet facebook pushes us to use jest which is facebook creation and it seems like they are forming an opinion in a tool that should be unopinionated. Anyways instead of complaining we saw that we can easily configure our environment to use other testing frameworks without ejecting the webpack configuration. Lets use CRA to bootstrap our next react application. Happy coding!