Create a single React bundle.js – detailed and complete guide with the force of npm and webpack

Table of contents:

There really is a simple way to create a working react app inside your existing project using Webpack.

We will create React app from scratch, without "create-react-app" template. For this task we need Webpack, a popular module bundler which will help to bundle our project files.

Let's make a new project and call it "webpackapp". Create a webpackapp folder:

mkdir webpackapp && cd webpackapp

npm setup

First of all, we need to install modules we want in our project. We will do this with npm. What is npm? It's a CLI (command-line interface) tool for publishing and downloading packages.

Add a file package.json (npm configuration file) in our project folder:

{
  "name": "webpackapp",
  "description": "A React.js project using Webpack",
  "version": "1.0.0",
  "author": "techapple.ru",
  "scripts": {
    "dev": "webpack-dev-server --hot --open",
    "build": "webpack",
    "build:dev": "webpack --mode=development"
  },
  "dependencies": {
    "css-loader": "^5.0.1",
    "file-loader": "^6.2.0",
    "react": "^16.14.0",
    "react-dom": "^16.5.0",
    "style-loader": "^2.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.1.2",
    "@babel/plugin-proposal-class-properties": "^7.12.1",
    "@babel/preset-env": "^7.1.0",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.0",
    "html-webpack-plugin": "^4.5.0",
    "node-sass": "^4.14.1",
    "sass-loader": "^10.1.0",
    "webpack": "^4.44.2",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.9"
  }
}

Dependencies section contains react and react-dom fields. These are modules containing core functions for React.js library, we need them for our app to work.

devDependencies section defines modules we need in our development mode.

  • @babel - compiler for jsx and js files
    • @babel/core - main compiler module
    • babel/loader and @babel/reset-react are used for loading react files into compiler
    • @babel/preset-env  is used for ES2015+ standard support
    • @babel/plugin-proposal-class-properties ??

As we use Webpack, at the end we add webpack module. To simplify development we need another module - webpack-dev-server. It will create dynamic web-server with hot reload (you won't need to hit refresh every time).

Besides that, we defined two commands in the scripts section: dev and build.

Command dev is used for development purposes and it let you generate bundle file on the fly and launch the project in a browser. We pass 2 parameters: --hot --open. Parameter --hot allows to enable Hot Module Replacement, it will re-render on component changes. Second parameter --open will launch default browser with our app (on "localhost" domain).

Command build will generate production bundle (bundle.js).

Full and detailed info about package.json you can find here.

Installing environment with npm

Now we tell npm to install all needed modules we defined in package.json:

npm install

When npm will finish install we can create some React project files.

Note that if you want to install some other module with a command, like for example, npm install bootstrap, npm will add dependencies in package.json itself. You don't need to modify package.json, it's that easy.

React app and component files

Add index.html file to the webpackapp folder:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Webpack && React</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>

We do not set script bundle.js, because html-webpack-plugin will do it for us later.

Time to add our app and component files. Create a folder named app, in which we will store our application files.

Create a subfolder components in app folder. Add new file SearchPlugin.jsx:

import React from 'react';

export default class SearchPlugin extends React.Component{

    constructor(props){
        super(props);
        this.onTextChanged = this.onTextChanged.bind(this);
    }

    onTextChanged(e){
        var text = e.target.value.trim();   // удаляем пробелы
        this.props.filter(text); // передаем введенный текст в родительский компонент
    }

    render() {
        return <input placeholder="Search" onChange={this.onTextChanged} />;
    }
}

And also in catalog "app/components" we add component ItemsList.jsx:

import React from 'react';
import SearchPlugin from './SearchPlugin.jsx';

export default class ItemsList extends React.Component {
    constructor(props){
        super(props);
        this.state = { items: this.props.data.items};

        this.filterList = this.filterList.bind(this);
    }

    filterList(text){
        var filteredList = this.props.data.items.filter(function(item){
            return item.toLowerCase().search(text.toLowerCase())!== -1;
        });
        this.setState({items: filteredList});
    }

    render() {
        return(
            <div>
                <h2>{this.props.data.title}</h2>
                <SearchPlugin filter={this.filterList} />
                <ul>
                    {
                        this.state.items.map(function(item){
                            return <li key={item}>{item}</li>
                        })
                    }
                </ul>
            </div>);
    }
}

In the first line we are importing React module, which contains React.Class. We need it for creating our components.

import React from 'react';

And we need first component - SearchPlugin which is defined in another file, so we import it also.

import SearchPlugin from './SearchPlugin.jsx';

The only difference between these two lines is in that react is installed as external module, so we pass only its name. Our component SearchPlugin is defined by us and we use a relative path (./ path means the file is located in the same folder).

Then in webpackapp folder we create our main project file app.jsx:

import ReactDOM from 'react-dom';
import React from 'react';
import ItemsList from './components/ItemsList.jsx';

const propsValues = {
    title: "Phones list",
    items: [
        "HTC U Ultra",
        "iPhone 7",
        "Google Pixel",
        "Huawei P9",
        "Meizu Pro 6",
        "Asus Zenfone 3"
    ]
};

ReactDOM.render(
    <ItemsList data={propsValues} />,
    document.getElementById("app")
)

This React file will actually load our components on a web page.

Finally, Webpack configuration

Add webpack.config.js to the webpackapp folder:

const HtmlWebPackPlugin = require( 'html-webpack-plugin' );
const path = require( 'path' );

module.exports = {
    entry: "./app.jsx", // entry point, our main app file
    output:{
        path: path.resolve(__dirname, './public'),     // path for our output files
        filename: "bundle.js"       // bundle filename
    },
    module:{
        rules:[   // jsx loader
            {
                test: /\.(js|jsx)$/, // determine file type
                exclude: /(node_modules)/,  // exclude node_modules folder
                loader: "babel-loader",   // determine loader
                options:{
                    presets:["@babel/preset-env", "@babel/preset-react"],    // used plugins
                    plugins: [
                      [
                        "@babel/plugin-proposal-class-properties"
                      ]
                    ]
                }
            },
            {
                test: /\.scss$/,
                loaders: ['style-loader', 'css-loader', 'sass-loader'] // scss loaders
            },
            {
                test: /\.(png|j?g|svg|gif)?$/,
                use: 'file-loader' // static files loader
            }
        ]
    },
   plugins: [
      new HtmlWebPackPlugin({ // html file generating for our setup
         template: path.resolve( __dirname, 'index.html' ), // path to the template file
         filename: 'index.html' // generated filename
      })
   ]
}

In Entry section "./app/app.jsx" we define the main app file.

Ouput section defines configuration for output file, its folder and name - public/bundle.js. This will be our final bundle file.

Module section defines a set of loaders, which will apply to different files (babel for jsx, file-loader for static files etc).

More about Webpack

Finally, the project structure will look like this:

Development test

Run this command for development build:

npm run dev

As a result the app will launch in a browser:

On this step you can easily debug your app, because React dev js files have a detailed information about errors in your code (of course, if you have some).

Production build

Now we can generate our final bundle.js with this command:

npm run build

It will create 2 files: index.html and bundle.js. You can put index.html directly to your browser and see the result. The final folder structure will be:

Folder public contains our production bundle.js

So, we built a bundle.js file, that we can use anywhere, include it in existing project for example. But don't forget to use appropriate container id in your template file (for our example app):

<div id="app"></div>

Thats it, in the next article I will explain how to create separate bundles for a project.

  •  
  •  
  •  
  •  
  •  
  •