September 24, 2019

Start a react project without create-react-app

Let’s be honest , boilerplates are great and create-react-app was one of the most awaited command as it allows you to start coding straight away without having to worry about all configurations that goes on .

But sometimes it’s good to understand how all the pieces connect , so as you can have a better understanding of how react works .

In this tutorial , we will learn the nuts and bolts that goes on when you bootstrap a react project by creating a new project without the create-react-app command .

Let’s get started , go to the folder where you want to run your project and create a new directory called created-react-app .

Make sure you have nodejs and npm installed . run below command to make sure node and npm are installed

node -v
npm -v

Let’s start our project but initializing a node project with the below command :

npm init -y

*The -y allows us to start a node project with the default configurations .

1 . Install babel
React uses JSX as its markup language . babel is the javascript compiler that will translate JSX to ordinary javascript .

Babel also allows us to use future versions of ECMA Script (ES2015 and beyond) .Let’s start using babel by adding babel core to our project .

npm i @babel/core

For it to be useful , babel would require some configurations to be added for supporting various libraries and functionalities . We will add the following plugins :-

The react preset

  • This allows conversion of jsx to plain javascript .

npm i --save-dev @babel/preset-react

The env preset

-This allows support for ES2015 (ES6) features .

npm i --save-dev @babel/preset-env

For our newly added presets to work in our project , we need to create a babel configuration file (.babelrc) on the root of our project and add the presets inside that file .

.babelrc

{
    "presets":["@babel/preset-react", "@babel/preset-env"]
}

Let’s us test if our new babel presets were applied , create app.js file , and add a simple function that uses ES6 Arrow function to test if our babel configurations works as expected !

const app = () => {
    console.log("Hello World from created react app ");
};

To run this file with babel , we will need to install babel-cli .

npm install --save-dev @babel/cli

@babel/cli includes an executable called babel that you can run with npx.

npx babel app.js

Running the command the output becomes

"use strict";

var App = function App() {
  console.log("Hello World from created react app ");
};

You can see some changes , our arrow function has been replaced with function and const keyword which was new in ES6 was replaced by var .

Let’s now test if our react-preset works as expected , change app.js file as below

const App = () => {
<h1 className="Hello">Hello World</h1>;
};

Run

npx babel app.js

The output will be as below

"use strict";

var App = function App() {
  /*#__PURE__*/
  React.createElement("h1", {
    className: "Hello"
  }, "Hello World");
};

As we can see , preset-react converted our h1 tag to a call to React.createElement().

2 . Install react

Now that we already configured babel , it’s time to jump in to the good stuffs .

Go on an install react and react-dom so as we can create our first react component :-)

npm install react react-dom

ReactDOM connect react and the DOM where in most cases it will be used only once during mounting with ReactDOM.render() function .

To make this tutorial as relevant as possible , let us copy the folder structure of create react app

created-react-app/
   README.md
   node_modules/
   package.json
   public/
      index.html
      favicon.ico
   src/
      App.css
      App.js
      App.test.js
      index.css
      index.js
      logo.svg

We won’t have all the files as create-react-app but only the ones necessary to run a react project .

Let’s start with the index.html file , create a public folder and inside it add index.html file with the following content :-

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Created React App</title>
</head>
<body>
    <noscript>You need to enable JavaScript to run this app.   </noscript>
    <div id="root"></div>
</body>
</html>

All of our source files will reside inside a src directory . Create a src directory and inside it let’s create our first file , index.js .

we will use ReactDOM.render() to mount react inside a parent container with id of root as below ;

import React from "react";
import ReactDOM from "react-dom";
import App from "./app";
ReactDOM.render(<App />, document.getElementById("root"));

Let us end with our last file , App.js which will only display a message “Hello World from created react app ” from the screen .

const App = () => {
    <h1>Hello World from created react app</h1>;
};
export default App;

Now everything should be fine , the last thing is to launch our app . For that we will use Webpack .

3 . Webpack

Here comes another headache to most newbies even experienced developers .

Webpack is a build tool that allows us to take to take all of our project assets and turns them in to a production ready bundle .

The files that our project uses are called modules and we tell webpack to load the modules when we configure our project .

The famous “Convention over Configuration” should apply here as this process can be very tedious .

But hey , wait a minute , going through webpack documentation for the latest version (Version 4) , here is what you can find

Since version 4.0.0, webpack does not require a configuration file to bundle your project. Nevertheless, it is incredibly configurable to better fit your needs.

Okay! , fingers crossed . let us proceed . We need to install the following npm packages before we proceed .

Webpack

This is module bundler for our application . As the application gets bigger , webpack will intelligently bundle our files in to a single file .

npm install -D webpack

Webpack-cli As its name , its a cli tool that will allows us to run webpack commands .

npm install -D webpack-cli

Let’s build our project Now that we have webpack and webpack-cli installed , its time to build our project to production . Note below if we choose to proceed without creating a webpack config file .

Out of the box, webpack won’t require you to use a configuration file. However, it will assume the entry point of your project is src/index and will output the result in dist/main.js minified and optimized for production.

Open package.json file under script add a build script for webpack

{
   "scripts":{
       "build" : "webpack"
   }
}

Run the build command

npm run build

If you run the command , you will see this error

ERROR in ./src/index.js 4:16
Module parse failed: Unexpected token (4:16)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| import ReactDOM from "react-dom";
| import App from "./app";
> ReactDOM.render(<App />, document.getElementById("root"));

This happens because a loader is missing in our webpack config . Webpack loaders helps with tasks such as perform transformations on files and loading of files and images . For example React uses JSX which a browser does not understand , so it needs to be transformed to plain javascript for it to run on a browser .

Go on and install babel-loader which is a webpack helper package that allows transpiling of JavaScript files using Babel and webpack.

npm install -save-dev babel-loader

Once installed , create webpack configuration file inside our project directory and save it as webpack.config.js . Inside our config file let’s add babel-loader with below configurations :

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-react", "@babel/preset-env"],
          },
        },
      },
    ],
  },
};

Now run npm run build , and it should compile succesfully . Go on and open a dist folder and you will see only one file , main.js which is our bundle file .

For our application to run we need index.html file , so the next thing to do is to tell webpack to include index.html file on our build as well as including our bundled file (main.js) inside the script tag of index.html .

There are two ways of doing this , the first one is manually by adding index.html file inside a dist folder as below

<!DOCTYPE html>
<html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />     <title>Created React App</title>
</head>
<body>
    <div id="root"></div>
    <script src="main.js"></script>
</body>
</html>

If you go on and click the index.html file , you should be able to open the page as below :

Another option , which is the ideal one is to use a plugin . go on and install html-webpack-plugin . This plugin will generate an HTML5 file for you that includes all your webpack bundles in the body using script tags.

npm i --save-dev html-webpack-plugin

After installing , just add the plugin to your webpack config as follows:

const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-react", "@babel/preset-env"],
          },
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
  ],
};

Now , if you build again , you should see the same result as the first screen .

Running the Dev Server

Another awesome feature of webpack is that it allows you to set up a development server that will automatically reload your project when things change .

To achieve that , we are going to install another plugin , called webpack-dev-server .

npm install --save-dev webpack-dev-server

Add the dev server config to our webpack.config.js file

var path = require('path');

module.exports = {
  //...
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000
  }
};

After that , in our package.json file , and a start script that uses our dev server .

"scripts": {
   "start": "webpack-dev-server"
 }

Note : If your running webpack version 5 replace webpack-dev-server with webpack serve as below

"scripts": {
   "start": "webpack serve"
 }

After that , go ahead and run

npm start

If everything goes well , you will see something like below on your command window , go ahead and navigate to http://localhost:9000/ , and you should be able to see the screen with “Hello World from created react app” message .

ℹ 「wds」: Project is running at http://localhost:9000/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /Users/robert/projects/articles/created-react-app/dist

Last thing is to test if our automatic reload feature works , go ahead and edit app.js file and change the message to”Hello World from react ” and the page should automatically reload .

Let’s end here as its already long content to digest , what you should explore after this is how to use webpack to also load css and images and also do code splitting , subject which i will cover in coming articles .

That’s it folks . Hope you have learned something and don’t hesitate to give feedback on this article.

Buy Me A Coffee

Robert Rutenge
Front-end Developer | ReactJS enthusiast | Problem Solver . Find me on twitter and Github