Blog

Create a React Project From Scratch Without any Framework

January 6, 2023 - Roy Derks

This blog post will guide you through the process of creating a new single-page React application from the ground up. We will begin by setting up a new project using Webpack and Babel. Building a React project from scratch will give you a strong foundation and understanding of the fundamental requirements of a project, which is essential for any project you may undertake before jumping into a framework like Next.js or Remix.

Click the image below to watch the YouTube video version of this blog post:

This blog post is extracted from my book React Projects, available on Packt and Amazon.

Setting up a new project

Before you can start building your new React project, you will need to create a new directory on your local machine. For this blog (which is based upon the book React Projects), you can name this directory 'chapter-1'.

To initiate the project, navigate to the directory you just created and enter the following command in the terminal:

npm init -y

This will create a package.json file with the minimum information required to run a JavaScript/React project. The -y flag allows you to bypass the prompts for setting project details such as the name, version, and description.

After running this command, you should see a package.json file created for your project similar to the following:

{
  "name": "chapter-1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Now that you have created the package.json file, the next step is to add Webpack to the project. This will be covered in the following section.

Adding Webpack to the project

In order to run the React application, we need to install Webpack 5 (the current stable version at the time of writing) and the Webpack CLI as devDependencies. Webpack is a tool that allows us to create a bundle of JavaScript/React code that can be used in a browser. Follow these steps to set up Webpack:

  1. Install the necessary packages from npm using the following command:
npm install --save-dev webpack webpack-cli
  1. After installation, these packages will be listed in the package.json file and can be run in our start and build scripts. But first, we need to add some files to the project:
chapter-1
|- node_modules
|- package.json
   |- src
      |- index.js

This will add an index.js file to a new directory called src. Later, we will configure Webpack so that this file is the starting point for our application.

  1. Add the following code block to this file:
console.log('Rick and Morty');
  1. To run the code above, we will add start and build scripts to our application using Webpack. The test script is not needed in this case, so it can be removed. Also, the main field can be changed to private with the value of true, as the code we are building is a local project:
{
  "name": "chapter-1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack --mode=development",
    "build": "webpack --mode=production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

The npm start command will run Webpack in development mode, while npm run build will create a production bundle using Webpack. The main difference is that running Webpack in production mode will minimize our code and reduce the size of the project bundle.

  1. Run the start or build command from the command line; Webpack will start up and create a new directory called dist.
chapter-1
|- node_modules
|- package.json
   |- dist
      |- main.js
   |- src
      |- index.js
  1. Inside this directory, there will be a file called main.js that includes our project code and is also known as our bundle. If successful, you should see the following output:
asset main.js 794 bytes [compared for emit] (name: main)
./src/index.js 31 bytes [built] [code generated]
webpack compiled successfully in 67 ms

The code in this file will be minimized if you run Webpack in production mode.

  1. To test if your code is working, run the main.js file in your bundle from the command line:
node dist/main.js

This command runs the bundled version of our application and should return the following output:

> node dist/main.js
Rick and Morty

Now, we're able to run JavaScript code from the command line. In the next part of this blog post, we will learn how to configure Webpack so that it works with React.

Configuring Webpack for React

Now that we have set up a basic development environment with Webpack for a JavaScript application, we can begin installing the packages necessary to run a React application. These packages are react and react-dom, where the former is the core package for React and the latter provides access to the browser's DOM and allows for rendering of React. To install these packages, enter the following command in the terminal:

npm install react react-dom

However, simply installing the dependencies for React is not enough to run it, since by default, not all browsers can understand the format (such as ES2015+ or React) in which your JavaScript code is written. Therefore, we need to compile the JavaScript code into a format that can be read by all browsers.

To do this, we will use Babel and its related packages to create a toolchain that allows us to use React in the browser with Webpack. These packages can be installed as devDependencies by running the following command:

In addition to the Babel core package, we will also install babel-loader, which is a helper that allows Babel to run with Webpack, and two preset packages. These preset packages help determine which plugins will be used to compile our JavaScript code into a readable format for the browser (@babel/preset-env) and to compile React-specific code (@babel/preset-react).

Now that we have the packages for React and the necessary compilers installed, the next step is to configure them to work with Webpack so that they are used when we run our application.

npm install --save-dev @babel/core babel-loader @babel/preset-env @babel/preset-react

To do this, configuration files for both Webpack and Babel need to be created in the src directory of the project: webpack.config.js and babel.config.json, respectively. The webpack.config.js file is a JavaScript file that exports an object with the configuration for Webpack. The babel.config.json file is a JSON file that contains the configuration for Babel.

The configuration for Webpack is added to the webpack.config.js file to use babel-loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
    ],
  },
};

This configuration file tells Webpack to use babel-loader for every file with the .js extension and excludes files in the node_modules directory from the Babel compiler.

To utilize the Babel presets, the following configuration must be added to babel.config.json:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "esmodules": true
        }
      }
    ],
    [
      "@babel/preset-react",
      {
        "runtime": "automatic"
      }
    ]
  ]
}

In the above @babel/preset-env must be set to target esmodules in order to use the latest Node modules. Additionally, defining the JSX runtime to automatic is necessary since React 18 has adopted the new JSX Transform functionality.

Now that we have set up Webpack and Babel, we can run JavaScript and React from the command line. In the next section, we will write our first React code and run it in the browser.

Rendering React components

Now that we have installed and configured the packages necessary to set up Babel and Webpack in the previous sections, we need to create a real React component that can be compiled and run. This process involves adding some new files to the project and making changes to the Webpack configuration:

  1. Let's edit the index.js file that already exists in our src directory so that we can use react and react-dom. Replace the contents of this file with the following:
import ReactDOM from 'react-dom/client';

function App() {
  return <h1>Rick and Morty</h1>;
}

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App />);

As you can see, this file imports the react and react-dom packages, defines a simple component that returns an h1 element containing the name of your application, and has this component rendered in the browser with react-dom. The last line of code mounts the App component to an element with the root ID selector in your document, which is the entry point of the application.

  1. We can create a file that has this element in a new directory called public and name that file index.html. The document structure of this project should look like the following:
chapter-1
  |- node_modules
  |- package.json
  |- babel.config.json
  |- webpack.config.js
  |- dist
    |- main.js
  |- public
    |- index.html
  |- src
    |- index.js
  1. After adding a new file called index.html to the new public directory, we add the following code inside it:
<!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>Rick and Morty</title>
  </head>
  <body>
    <section id="root"></section>
  </body>
</html>

This adds an HTML heading and body. Within the head tag is the title of our application, and inside the body tag is a section with the "root" ID selector. This matches the element we have mounted the App component to in the src/index.js file.

  1. The final step in rendering our React component is extending Webpack so that it adds the minified bundle code to the body tags as scripts when running. To do this, we should install the html-webpack-plugin package as a devDependency:
npm install --save-dev html-webpack-plugin

To use this new package to render our files with React, the Webpack configuration in the webpack.config.js file must be updated:

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

Now, if we run npm start again, Webpack will start in development mode and add the index.html file to the dist directory. Inside this file, we'll see that a new scripts tag has been inserted inside the body tag that points to our application bundle – that is, the dist/main.js file.

If we open this file in the browser or run open dist/index.html from the command line, it will display the result directly in the browser. The same is true when running the npm run build command to start Webpack in production mode; the only difference is that our code will be minified:.

This process can be sped up by setting up a development server with Webpack. We'll do this in the final part of this blog post.

Setting up a Webpack development server

While working in development mode, every time we make changes to the files in our application, we need to rerun the npm start command. This can be tedious, so we will install another package called webpack-dev-server. This package allows us to force Webpack to restart every time we make changes to our project files and manages our application files in memory instead of building the dist directory.

The webpack-dev-server package can be installed with npm:

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

Also, we need to edit the dev script in the package.json file so that it uses webpack- dev-server instead of Webpack. This way, you don't have to recompile and reopen the bundle in the browser after every code change:

{
  "name": "chapter-1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack serve --mode=development",
    "build": "webpack --mode=production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

The preceding configuration replaces Webpack in the start scripts with webpack-dev-server, which runs Webpack in development mode. This will create a local development server that runs the application and ensures that Webpack is restarted every time an update is made to any of your project files.

To start the local development server, just enter the following command in the terminal:

npm start

This will cause the local development server to be active at http://localhost:8080/ and refresh every time we make an update to any file in our project.

Now, we have created the basic development environment for our React application, which you can further develop and structure when you start building your application.

Conclusion

In this blog post, we learned how to set up a React project with Webpack and Babel. We also learned how to render a React component in the browser. I always like to learn a technology by building something with it from scratch before jumping into a framework like Next.js or Remix. This helps me understand the fundamentals of the technology and how it works.

This blog post is extracted from my book React Projects, available on Packt and Amazon.

I hope you learned some new things about React! Any feedback? Let me know by connecting to me on Twitter. Or leave a comment on my YouTube channel.