DEV Community

Cover image for Create-react-app with VS Code
Thibaud Ducasse
Thibaud Ducasse

Posted on • Edited on • Originally published at tducasse.com

Create-react-app with VS Code

Set up a basic React development environment using create-react-app and VS Code

Requirements

Note: I uploaded the final code on GitHub.

Create a new project using create-react-app

create-react-app provides a very easy way to generate a React app in seconds. This is currently developed by Facebook, and the recommended way to start a new project.
Open a terminal and go to the desired folder (create-react-app will create a folder for your app).

cd ~/Projects # this is my projects folder
npx create-react-app my-app # replace `my-app` with the name of your app
cd myapp
npm start
Enter fullscreen mode Exit fullscreen mode

You should see something like this:
react-app.png

Folder structure

The script will generate a few files, and it should look something like this:
architecture.png

Let's have a look at what's been created:

  1. package.json: This file is related to npm. It holds our dependencies (the libraries you want to be able to use in your code). You can also describe your application, add useful scripts in order to automate common tasks, such as running tests, starting a server, deploying, etc.
  2. package-lock.json: This is auto-generated by npm every time you add a new dependency. You should never have to touch it, and it is good practice to commit it to your repository.
  3. .gitignore: When you commit files to a git repository, git will (by default) want to add every file that's in your project. But sometimes you might want to exclude a file, and .gitignore is exactlly that. By default, create-react-app excludes node_modules for example, which is the folder containing all our dependencies. Since we are going to commit package.json and package-lock.json anyway, there is no need at all to commit them (plus they are super heavy).
  4. public folder: This is where we find the root of our application, index.html, and our favicon, favicon.ico.
  5. src folder: Our app folder. This is where our code lives, and where we are going to spend 98% of our time.

Let's have a look at the code

First, let's go to index.html, and have a look at this line:

...
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
// the next line
    <div id="root"></div>

...
Enter fullscreen mode Exit fullscreen mode

This will be the HTML element to which the React application will be attached.

index.js is the first file that will be loaded in our application.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

// the next line
ReactDOM.render(<App />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

This line is the one that actually links our React app to the DOM (root node, as we've seen just before).

// the two next lines
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

These two first lines are everything that's necessary to write React code.

import React from 'react';
import ReactDOM from 'react-dom';
// the two next lines
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

These ones are just importing our React components (here App), and our CSS files.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// the next line
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
Enter fullscreen mode Exit fullscreen mode

This last line is used only if you are writing a PWA (Progressive Web App, more on this here). Yes, create-react-app is already configured as a PWA!

So what is this App.js doing? Let's have a look:

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

At the beginning, you can see the standard import React from 'react' that you need to include in every file that uses jsx (this cool HTML-like syntax that we use to write React components).
You may have noticed that we import a file called App.css.
Open it, and look at what's written here:

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 40vmin;
  pointer-events: none;
}

.App-header {
  background-color: #282c34;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color: white;
}

.App-link {
  color: #61dafb;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

Enter fullscreen mode Exit fullscreen mode

I'm not going to spend too much time on this, but let's take the example of the first class:

.App {
  text-align: center;
}
Enter fullscreen mode Exit fullscreen mode

And let's see how we use it in a React component:

function App() {
  return (
    // the next line
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the main difference with "normal" HTML is that the name of the attribute is className, instead of class.

Configuring VS Code

Enough about create-react-app and the code it generated, it's time to configure our code editor.
VS Code has pretty good defaults (syntax highlighting, autocompletion, 'go to definition', etc). But you can make it even better!

ESlint

Since JavaScript is not a compiled language, there is nothing that can tell us if something looks like it's going to fail before we actually run it. ESLint solves exactly this problem.

ESLint is the most popular JavaScript linter. A linter is a program that analyses your code, and tries to find potential errors, ahead of runtime. ESLint is highly configurable, and you can extend premade sets of rules, define your own rules, override existing rules, etc.

Luckily, create-react-app already comes with ESlint. So we don't have much to do!

Install the ESLint extension for VS Code. This will allow us to see the result of ESLint directly in VS Code.

Once installed, you can quickly test it. Go to App.js again, and remove this line:

<a
    className="App-link"
    href="https://reactjs.org"
    target="_blank"
    // the next line
    rel="noopener noreferrer"
  >
Enter fullscreen mode Exit fullscreen mode

VS Code should start yelling at you:
eslint.png

Prettier

Prettier is an opinionated code formatter. People used to fight for hours around commas, semi-colons, etc. Prettier is an attempt at ending the debate. Since most editors have a Prettier plugin, you can "autoformat on save", which means you can write ugly code, and never worry about formatting!

You can use Prettier in different ways. My favorite one is to run it as part of the ESLint rules.

First, install the Prettier dependencies:

npm i -D prettier eslint-config-prettier eslint-plugin-prettier
Enter fullscreen mode Exit fullscreen mode

Then, create a file, at the root of your app, called .eslintrc.json as such:

{
  "extends": [
    "react-app",
    "prettier",
    "prettier/react"
  ],
  "plugins": [
    "prettier"
  ],
  "rules": {
    "prettier/prettier": "error"
  }
}
Enter fullscreen mode Exit fullscreen mode

So what have we done?

  • eslint-config-prettier is a package that allows us to disable the rules that would conflict with the rules defined by Prettier.
  • eslint-plugin-prettier is the one that allows to run Prettier as an ESLint rule.

If you have a look at the .eslintrc.json file that we just created, you'll notice that we have added a rule that basically says "everything that Prettier reports should be treated as an error".

Once everything is saved, go back to App.js and have a look at what ESLint is saying now:
eslint-prettier.png

Autoformatting

So this is all good, now we see what's wrong about our code, but wouldn't it be nice if we could just fix everything automatically? Replacing double quotes with single quotes should be pretty straightforward for a computer, right?

ESLint has an option to autofix every error that can be autofixed. On the command-line, it's --fix, and you can configure your VS Code so that this happens everytime you save.

Create a new folder, at the root of your app, called .vscode, and inside this folder, a file called settings.json:

{
  "eslint.autoFixOnSave": true
}
Enter fullscreen mode Exit fullscreen mode

Go back to App.js, and try saving your file now, it should get reformatted instantly!

Precommit hook

So now that we have linting and formatting all sorted, what happens if someone decides to contribute to our code without setting up everything we just set up? It will break everything, and you will be back to coding-style hell. So what can we do about it?

Two packages will help us with that:

  • husky gives us a really easy way to set up git hooks.
  • lint-staged will lint the files that are ready to be committed.

First, install them with:

npm i -D lint-staged husky
Enter fullscreen mode Exit fullscreen mode

Go to your package.json and add:

"lint-staged": {
    "**/*.js": [
      "eslint --fix",
      "git add"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }
Enter fullscreen mode Exit fullscreen mode

And you're done! Now, every time you will try to commit unformatted files, husky will launch lint-staged, which will intercept the process and run ESLint first. And if there's an error ESLint can't fix, it will stop the whole process. Which means you can't commit code that doesn't work anymore!

Absolute imports

It is very common in a React project to organise your code in multiple nested folders.

Let's say we decide to implement something like Brad Frost's atomic design for example. A common way to implement it would be (this example is on GitHub):
atomic.png

App.js would import the LoginPage template like this:

import React from "react";
// the next line
import LoginPage from "./components/templates/LoginPage";

function App() {
  return (
    <div style={{ padding: 20 }}>
      <LoginPage />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This is all good! But now, if you go to LoginPage.js, and have a look at how we have to import LoginPassword:

import React, { useState } from "react";
// the next line
import LoginPassword from "../organisms/LoginPassword";

const LoginPage = props => {
  return (
    <>
      <LoginPassword />
    </>
  );
};

export default LoginPage;
Enter fullscreen mode Exit fullscreen mode

Notice the ../ to go up a folder? This will become really hard to follow once we start deeply nesting folders and files.

A good solution for that is something called absolute imports. At the root of your app, create a file called jsconfig.json:

{
  "compilerOptions": {
    "baseUrl": "./src/"
  },
  "include": [
    "src"
  ]
}
Enter fullscreen mode Exit fullscreen mode

Now you can import your files using an absolute path, starting from ./src/:

import React from "react";
// the next line
import LoginPassword from "components/organisms/LoginPassword";

const LoginPage = () => {
  return (
    <>
      <LoginPassword />
    </>
  );
};

export default LoginPage;
Enter fullscreen mode Exit fullscreen mode

While it might not seem like a big deal right now, because we only have one level of nesting, big applications tend to have very deeply nested components, which makes imports look like ../../../../MyComponentFolder/MyComponent very quickly!

Find the final code on GitHub.

Top comments (0)