Create a React app from scratch with Webpack and Babel (2023)

This article will guide you through the process of creating a React app fromscratch. It is meant for developers who want a better understanding of how toolslike Babel, Webpack, DevServer, React, loaders, and presets make up amodern React app. We'll try to understand how they all fit together byincrementally piecing together an application.

The article was updated in 2022 for Webpack 5 and Babel 7.

Bootstrapping your own React app from scratch can be confusing. You don'tsimply run React in a browser. You need to bundle the different parts of yourapplication together and transform your code into something the browser canunderstand.

Luckily there are some great tools that help you with that. You've probablyheard of Webpack, a popular module bundler, and Babel, a tool that compiles"next generation" JavaScript into a backwards compatible version.

You could start your project by copying boilerplate code from a blog post or aGithub repository. But I would suggest you do one of the following instead:

  1. Create your own boilerplate code and try to understand each step of theconfiguration process.
  2. Use create-react-app, apopular tool that lets you set up a React app with just one command. Youdon't need to get your hands dirty with Webpack or Babel because everythingis preconfigured and hidden away from you.

While create-react-app is a great tool, the purpose of this article is tocreate our own boilerplate.

Let's jump right in.

Initialize the project

(This article has a companion Git repository:github.com/nicoqh/react-boilerplate)

We'll start by creating a basic directory structure for our project. We need adirectory for our source files which we'll call src. We also need a directoryfor compiled assets like JavaScript and HTML files. We'll call this directorydist.

mkdir srcmkdir dist

The next step is to create a .gitignore file with the following content:

distnode_modules

This will instruct Git to ignore the node_modules directory and make sure wedon't accidentally commit every Node module we use in our project. We also wantto ignore the dist directory. Everything inside this directory will becompiled from the source files, so we don't need to add it to version control.

As with most Node-based projects, we need a package.json that lists ourdependencies. Simply add an empty object for now:

{}

Let's add some code to our src directory. Create the file index.js with thefollowing code:

// src/index.jsconst greet = (name) => console.log(`Hello, ${name}`);greet("Jon Snow");

This code uses arrow functionsand template literalswhich are ES6 features that don't yet work in every browser. The code needs tobe transpiled. This brings us to Babel.

Babel

What is Babel?

According to its website, "Babel is a toolchain that ismainly used to convert ECMAScript 2015+ code into a backwards compatible versionof JavaScript in current and older browsers or environments."

In other words we need Babel to transform modern JavaScript code into somethingbrowers can understand. We also need Babel to transform other "weird" stuff,like the JSX we will use with React, into something that makes sense to browsers.

To use Babel we need the compiler core and the Babel command line:

npm install --save-dev @babel/core @babel/cli

Now that Babel is installed in our node_modules folder, let's try to run it onour source and output the result to dist:

./node_modules/.bin/babel src --out-dir dist

Did anything happen? If you look inside the dist folder you'll see that ourcode hasn't changed at all. This is because we haven't told Babel what to do.Out of the box, Babel simply parses our code and returns it untouched.

Note: Instead of running the Babel executable located at./node_modules/.bin/babel, we can use a tool callednpx. npx makes it easier to execute localpackages. Usage: npx <command>.

Syntax transformations

For Babel to do anything useful we need to enable some plugins. Plugins areresponsible for transforming our code and parsing/understanding the syntax. Forexample, there's a plugin called @babel/plugin-transform-arrow-functions whichtransforms arrow functions to plain old JavaScript functions. However, insteadof installing a bunch of individual plugins (one for each transformation), Babeloffers something called "presets". You can think of a preset as an array ofplugins used to support a particular JavaScript language feature.

One of the official presets is called @babel/preset-env. This is a "smart"preset that allows us to use the latest JavaScript without needing to managewhich specific syntax transformations or polyfills are needed by your targetenvironments. (A "target environment" is an environment on which we want ourcode to run, e.g. Chrome 99.) The preset lets us specify a set of environments,and it will generate a list of plugins which it passes to Babel. With this listof plugins Babel will only transform language features that are notimplemented in the browsers we target. That leads to a smaller bundle and lesscode to parse.

Let's install @babel/preset-env:

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

Now, how do we specify our target environments?

@babel/preset-env integrates with Browserslist,a project that lets us specify target environments using queries. Babelrecommends putting the queries in a .browserslistrc file.

(Video) Create a React App WITHOUT Create React App

Let's create the .browserslistrc file:

# Browsers we support> 0.5%last 2 versionsnot dead

These queries will select the last 2 versions of every browser that is not"dead" and has a market share above 0.5%. You can read more about Browserslist'squery syntax at github.com/browserslist/browserslist

Let's run Babel again using the preset @babel/preset-env:

npx babel src --out-dir dist --presets=@babel/preset-env

Check out the dist folder—our code has been transformed! (Unless your targetenvironments support both arrow functions and template literals.)

"use strict";var greet = function greet(name) { return console.log("Hello, ".concat(name));};greet("Jon Snow");

Based on our Browserslist queries, @babel/preset-env applied the necessarytransform plugins (in this case @babel/plugin-transform-arrow-functions and@babel/plugin-transform-template-literals) to transform our code. If theselanguage features were already supported by the target environments, Babelwould have left the code untouched.

The Babel command will soon become cumbersome to manage (and to remember).Luckily Babel lets us specify our options in a configuration file.

Let's create the file babel.config.js and configure Babel to use@babel/preset-env so we don't have to specify it on the command line:

// babel.config.jsconst presets = [["@babel/preset-env"]];const plugins = [];// Export a config object.module.exports = { presets, plugins };

Babel will detect this file automatically. We no longer need to manually typeout our presets (or other configuration values) on the command line.

Let's try it:

npx babel src --out-dir dist

This should yield the same result as earlier.

It would be nice if we could see what plugins and presets were actually applied.preset-env takes a debug option which, if set to true, will instruct Babelto output the targets and plugins it uses during compilation. Update yourbabel.config.js to look like this (view commit):

// babel.config.jsconst presets = [ [ "@babel/preset-env", { // Pass a config object to the preset debug: true, // Output the targets/plugins used when compiling }, ],];const plugins = [];// Export a config object.module.exports = { presets, plugins };

Run Babel again and the output will be similar to this:

// npx babel src --out-dir dist@babel/preset-env: `DEBUG` optionUsing targets:{ "android": "98", "chrome": "96", "edge": "97", "firefox": "96", // ...}Using modules transform: autoUsing plugins: proposal-class-static-block { ie, ios, safari, samsung } proposal-private-property-in-object { ie, ios < 15, safari < 15, samsung } proposal-class-properties { ie, ios < 15 }

Polyfills

Certain modern language features can be "transformed" into older syntax. This isthe case with the arrow function above; it can be replaced by a plain oldJavaScript function. Other functionality needs to be added to the runtime as"polyfills". For this we use a library called core-js.

core-js is a standard library for JavaScript that includes polyfills for a widearray of JavaScript features. The library lets us use features like promises andsymbols in browsers that don't yet support them. By including a polyfill for alanguage feature, we can use the feature as if it were natively supported bythe browser.

Install core-js:

npm install --save core-js

Import core-js at the top of your src/index.js:

// src/index.jsimport "core-js/stable"; // Loads all language features// ... the rest of our code

We're now including every polyfill that is offered by core-js. Is thisnecessary? We don't want to polyfill features that are already supported by ourtarget environments. As mentioned earlier, @babel/preset-env uses Browserslistto include only the transformation plugins we need. @babel/preset-env can alsodecide what polyfills to include from core-js using the preset'suseBuiltIns option.

The useBuiltins option takes one of the following values:

  • 'entry': This will enable a plugin that transforms the import of core-js(import 'core-js/stable', like we did above), to imports of individualcore-js polyfills. Our target environments will determine which polyfills toimport. It doesn't matter if our app uses the language feature or not; aslong as the feature is missing from one of our target environments, thepolyfill is loaded.
  • 'usage': This option will add individual polyfill imports whenever alanguage feature is actually used in our source files. We don't need tomanually import anything. Whenever we use a feature that isn't supported byone of our target environments, a polyfill is imported in the file that needsit.

Let's use the 'usage' option so we don't have to worry about importingpolyfills.

Update your babel.config.js (view commit):

// babel.config.jsconst presets = [ [ "@babel/preset-env", { // Pass a config object to the preset debug: true, // Output the targets/plugins used when compiling // NEW CODE: // Configure how @babel/preset-env handles polyfills from core-js. // https://babeljs.io/docs/en/babel-preset-env useBuiltIns: "usage", // Specify the core-js version. Must match the version in package.json corejs: 3, // Specify which environments we support/target. We have chosen to specify // targets in .browserslistrc, so there is no need to do it here. // targets: "", // END NEW CODE }, ],];const plugins = [];// Export a config object.module.exports = { presets, plugins };

We also need to tell Babel which core-js version we're using. @babel/preset-envsupports both version 2 and 3. We have specified the version by settingcorejs: 3. This should match the version specified in your package.json.

Because we have set useBuiltIns: 'usage' we can remove theimport 'core-js/stable' statement from src/index.js. As you may remember,the 'usage' option takes care of importing any necessary polyfills.

Our current configuration will have the following effect on our code:

// Before transformationvar a = new Promise();// After transformation (if the environment doesn't support promises):import "core-js/modules/es.promise"; // The promise polyfill is imported ...var a = new Promise(); // ... so this will work.

(Side note: Are you wondering why we're not using @babel/polyfill? Thispackage has been deprecated in favor of importing core-js like we did above.)

That's it for Babel. Let's continue to Webpack.

Webpack

What is Webpack?

(Video) React Webpack Setup From Scratch

According to its website, Webpack is "a static modulebundler for modern JavaScript applications". Webpack creates a graph of everymodule our app uses (JavaScript files, React components, images, CSS files etc.),and generates one or more bundles. It's not uncommon to generate one bundlethat contains all the modules that make up an application.

Let's install Webpack and its command line tool (CLI):

npm install --save-dev webpack webpack-cli

Create the file webpack.config.js and add the following content (viewcommit):

// webpack.config.jsconst path = require("path");// We'll refer to our source and dist paths frequently, so let's store them hereconst PATH_SOURCE = path.join(__dirname, "./src");const PATH_DIST = path.join(__dirname, "./dist");// Export a configuration objectmodule.exports = { // Tell Webpack to do some optimizations for our environment (development // or production). Webpack will enable certain plugins and set // `process.env.NODE_ENV` according to the environment we specify. // https://webpack.js.org/configuration/mode mode: "development", // The point or points to enter the application. This is where Webpack will // start. We generally have one entry point per HTML page. For single-page // applications, this means one entry point. For traditional multi-page apps, // we may have multiple entry points. // https://webpack.js.org/concepts#entry entry: [path.join(PATH_SOURCE, "./index.js")], // Tell Webpack where to emit the bundles it creates and how to name them. // https://webpack.js.org/concepts#output // https://webpack.js.org/configuration/output // https://webpack.js.org/configuration/output#outputFilename output: { path: PATH_DIST, filename: "js/[name].[contenthash].js", // The public URL of the output dir when referenced in a browser. // This value is prefixed to every URL created by the runtime or loaders. // It's empty by default, which creates URLs like 'bundle.js' and results // in 404s if they're requested from a nested URL like /articles/1 // https://webpack.js.org/configuration/output/#outputpublicpath publicPath: "/", },};

This is a pretty basic Webpack configuration file. You should read through thecomments to get a sense of its structure.

Our entry point is ./src/index.js and our compiled bundle will be emitted to./dist/js/[name].[contenthash].js. Webpack will substitute [name] with the entryname, which is main by default. [contenthash] will be replaced with a hashof the module's (the file's) content. This is great for HTTP caching. We cantell browsers to cache JavaScript files aggressively. Whenever we re-build theapp with updated code, the bundle name (the hash) changes as well. This willbreak the browser's cache and force it to re-download the bundle.

Because we only have one entry point and only create one bundle, the output filecould have a static name, like bundle.js. But we'll keep it dynamic in case weneed multiple bundles later, and because [contenthash] is useful for caching.

Before we run Webpack with our newly created config, let's create annpm scriptso we don't need to type out the whole command.

Open your package.json and add the "scripts" section (view commit):

{ "scripts": { "dev": "webpack --config webpack.config.js" }, "devDependencies": { // ... the rest of package.json

Now we can simply run:

npm run dev

If we take a look in our dist directory we'll find a js directory with afile named main.[some-hash].js. This is our bundle. It contains a lot ofWebpack-specific code which we don't need to care about. Somewhere at the bottomyou'll also see our application code.

A new bundle will be generated every time we change the source code and run thenpm script npm run dev. Eventually the directory will be littered with oldbundles. We'll deal with this nuisance later. For now you can simply delete thedist folder regularly.

If you look closely in dist/js/main.[some-hash].js you'll notice that our codehasn't been transformed and that no polyfills have been loaded. This is becausewe haven't told Webpack to use Babel yet. We only ran the Webpack command, withno mention of Babel. We'll fix that soon, but first we'll create amodule to see how Webpack handlesso-called "bundling".

Create a file named sum.js in the source directory with the following contents:

// src/sum.jsconst sum = (a, b) => a + b;export default sum;

This is our first module. It consists of an arrow function that returns the sumof two numbers, a and b. This module can be imported from anywhere.

Let's use sum in src/index.js:

// src/index.jsimport sum from "./sum";console.log(sum(2, 4)); // Output: 6

Run Webpack again.

npm run dev

Just like before, our bundle ends up in dist/js/main.[some-hash].js. Itcontains all our code, including the imported sum module. Let's run our bundlewith Node to see if it works:

node dist/js/main.[some-hash].js

You should see 6 on the command line.

Building the application with Webpack works as expected, but we still need toinclude Babel in our build process. This brings us to a Webpack concept called"loaders".

Webpack Loaders

We have already configured Babel to run transformations on our code and importany necessary polyfills. But, at the moment, we're not running Babel, we're onlyrunning Webpack. Our next task is to tell Webpack how to include Babel in itsbuild process. This is achieved with "loaders".

Loaders are used to tell Webpack how to treat the different modules that weimport throughout our app. Using loaders we can tell Webpack what to do when weimport sum from './sum', import './styles/main.scss' andimport logo from './logo.png'.

For example, using a loader we can instruct Webpack to run .scss files through a Sasscompiler.

In other words, loaders are transformations that are applied on the source codeof a module. They allow us to pre-process files as we import or "load" them.

We want Webpack to run Babel on all our JavaScript modules. Whenever we import afile that ends in .js we want Webpack to use a Babel "loader". The Babelloader will run Babel on the imported code, and Babel will transform itaccording to our Babel configuration.

There's a loader conveniently called babel-loader which we'll install:

npm install --save-dev babel-loader

Next we'll update our Webpack configuration. We'll create a new section calledmodules in which we'll specify how our modules should be treated by Webpack.In this section we will add some rules which tell Webpack how and when to usethe loaders.

(Video) [2023 UPDATE] Create a React App With React 18, Webpack 5, Babel. No framework!

Update your webpack.config.js to reflect the following changes (viewcommit):

// webpack.config.js // ... output: { path: PATH_DIST, filename: 'js/[name].[hash].js', }, // NEW CODE: // Determine how the different types of modules will be treated. // https://webpack.js.org/configuration/module // https://webpack.js.org/concepts#loaders module: { rules: [ { test: /\.js$/, // Apply this rule to files ending in .js exclude: /node_modules/, // Don't apply to files residing in node_modules use: { // Use the following loader and options loader: "babel-loader", // We can pass options to both babel-loader and Babel. This option object // will replace babel.config.js options: { presets: [ ["@babel/preset-env", { debug: true, // Output the targets/plugins used when compiling // Configure how @babel/preset-env handles polyfills from core-js. // https://babeljs.io/docs/en/babel-preset-env useBuiltIns: "usage", // Specify the core-js version. Must match the version in package.json corejs: 3, // Specify which environments we support/target for our project. // (We have chosen to specify targets in .browserslistrc, so there // is no need to do it here.) // targets: "", }], ], }, } } ], }, // END NEW CODE};

You should read the comments as they explain most of what's going on.

Notice the options object that we pass to babel-loader. This object has beencopied directly from our babel.config.js. Instead of having Babel readbabel.config.js, we can pass our options through Webpack. As you'll seelater, it's very convenient to put our Babel configuration inside our Webpackconfiguration.

You can safely delete babel.config.js.

Test the new Webpack config by running the npm script we created earlier:

npm run dev

In summary, Webpack bundles our code. It also uses the "loader" babel-loaderto run Babel on any file (module) that ends in .js. Babel transforms our codeto a backward-compatible version.

Development vs production

Eventually we want to differentiate development builds from production builds.

Our Webpack config currently exports a configuration object. If we insteadexport a function, Webpack will invoke it with an environment as the firstargument. We can use this argument to include or exclude configuration optionsbased on whether we're building for production or development.

Consider this example:

// Exporting an object from webpack.config.js:// (This is what we're currently doing.)module.exports = { // Our Webpack config object.};// If we export a function, it will be passed two parameters, the first// of which is the webpack command line environment option `--env`.// `webpack --env a=b` sets env.a = 'b'// `webpack --env environment=production` sets env.environment = 'production'// https://webpack.js.org/configuration/configuration-types/#exporting-a-functionmodule.exports = (env) => { // Use the `env` argument to create some helpful constants. const environment = env.environment; const isProduction = environment === "production"; const isDevelopment = environment === "development"; return { // Our Webpack config object. // We now have access to the constants `environment`, // `isProduction` and `isDevelopment`. mode: environment, };};

Let's incorporate this into our Webpack config. Our new webpack.config.jsshould look like this (view commit):

// webpack.config.jsconst path = require("path");// We'll refer to our source and dist paths frequently, so let's store them hereconst PATH_SOURCE = path.join(__dirname, "./src");const PATH_DIST = path.join(__dirname, "./dist");// If we export a function, it will be passed two parameters, the first// of which is the webpack command line environment option `--env`.// `webpack --env.a = b` sets env.a = 'b'// `webpack --env.production` sets env.production = true// https://webpack.js.org/configuration/configuration-types/#exporting-a-functionmodule.exports = (env) => { const environment = env.environment; const isProduction = environment === "production"; const isDevelopment = environment === "development"; return { // Tell Webpack to do some optimizations for our environment (development // or production). Webpack will enable certain plugins and set // `process.env.NODE_ENV` according to the environment we specify. // https://webpack.js.org/configuration/mode mode: environment, // The point or points to enter the application. This is where Webpack will // start. We generally have one entry point per HTML page. For single-page // applications, this means one entry point. For traditional multi-page apps, // we may have multiple entry points. // https://webpack.js.org/concepts#entry entry: [path.join(PATH_SOURCE, "./index.js")], // Tell Webpack where to emit the bundles it creates and how to name them. // https://webpack.js.org/concepts#output // https://webpack.js.org/configuration/output // https://webpack.js.org/configuration/output#outputFilename output: { path: PATH_DIST, filename: "js/[name].[contenthash].js", }, // Determine how the different types of modules will be treated. // https://webpack.js.org/configuration/module // https://webpack.js.org/concepts#loaders module: { rules: [ { test: /\.js$/, // Apply this rule to files ending in .js exclude: /node_modules/, // Don't apply to files residing in node_modules use: { // Use the following loader and options loader: "babel-loader", // We can pass options to both babel-loader and Babel. This option object // will replace babel.config.js options: { presets: [ [ "@babel/preset-env", { debug: true, // Output the targets/plugins used when compiling // Configure how @babel/preset-env handles polyfills from core-js. // https://babeljs.io/docs/en/babel-preset-env useBuiltIns: "usage", // Specify the core-js version. Must match the version in package.json corejs: 3, // Specify which environments we support/target for our project. // (We have chosen to specify targets in .browserslistrc, so there // is no need to do it here.) // targets: "", }, ], ], }, }, }, ], }, };};

These changes will come in handy when we add more options and loaders later. Buthow do we pass the environment to Webpack? With the --env option:

webpack --env environment=production --config webpack.config.js

We can now create two different npm scripts, one for each environment. Change the"scripts" section of your package.json to look like this:

// package.json{ "scripts": { "build": "webpack --env.environment=production --config webpack.config.js", "dev": "webpack --env.environment=development --config webpack.config.js" }, // ...

To create a production build we simply run:

npm run build

And for development builds:

npm run dev

Adding index.html (HtmlWebpackPlugin)

This is our current directory structure:

├── src│ ├── index.js│ └── sum.js├── dist│ └── js│ └── main.[some-hash].js├── package.json├── package-lock.json├── webpack.config.js└── .browserslistrc

Eventually we want to deploy the dist directory to a server, but first we needan index.html which will serve as the entry point to our web application.

The HTML file should import the bundle, like this:

<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title>Boilerplate!</title> </head> <body> <script src="/js/main.c9eb7e60a479f1a2d6bc.js"></script> </body></html>

However, instead of putting a static HTML file inside dist, we want Webpack togenerate the file automatically. There are a few reasons for this:

  • We don't want to hardcode the bundle's file name which changes frequently.
  • We've told Git to ignore the dist directory (using .gitignore), soeverything we put inside it will be lost.
  • We may eventually want to include other dynamic content, like placeholders, inindex.html.

There's a Webpack plugin called HtmlWebpackPluginthat will generate HTML files for us. Let's install it:

npm install --save-dev html-webpack-plugin

First we need an HTML template for HtmlWebpackPlugin to use. Create index.htmland place it in the src directory:

<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Boilerplate!</title> </head> <body></body></html>

This file will be used as a basis for the generated index.html, so feel freeto add your own markup, like meta tags or Open Graph tags.

The <script> tag is omitted because it will be added by HtmlWebpackPluginautomatically.

To use the plugin we need to import it at the top of webpack.config.js. Wealso need to enable it by adding it to the plugins array of the Webpackconfiguration object. Since this is the first plugin we add, we need to createthe plugins array (view commit):

// webpack.config.jsconst path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin'); // NEW LINE// ... module: { // ... }, // NEW CODE: plugins: [ // This plugin will generate an HTML5 file that imports all our Webpack // bundles using <script> tags. The file will be placed in `output.path`. // https://github.com/jantimon/html-webpack-plugin new HtmlWebpackPlugin({ template: path.join(PATH_SOURCE, './index.html'), }), ], // END NEW CODE };};

(Notice the import of HtmlWebpackPlugin at the top.)

You can test the changes by creating a new build:

npm run build

The automatically generated index.html has been added to dist/index.html.

(Video) Create React App from Scratch using Webpack & Babel

Cleaning out the dist directory

You may have noticed that the dist directory has started to fill up with oldbundles. Let's remedy this by installing a Webpack plugin that automaticallycleans out the directory before every new build.

Install clean-webpack-plugin:

npm install --save-dev clean-webpack-plugin

Then, import the plugin at the top of webpack.config.js and add it to theplugins array (view commit):

// webpack.config.jsconst path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // NEW LINE// ... plugins: [ // ... // NEW CODE: // This plugin will delete all files inside `output.path` (the dist directory), // but the directory itself will be kept. // https://github.com/johnagan/clean-webpack-plugin new CleanWebpackPlugin(), // END NEW CODE ], };};

Webpack DevServer

Let's do a quick recap. Our bundle is emitted to the dist directory. Thedirectory also contains a generated index.html file which references ourbundle. The dist directory is actually ready to be deployed.

But there's one thing that quickly becomes annoying. We need to run Webpack eachtime we change our code. Is there a way to run our npm script, npm run dev,automatically whenever our code changes?

One way is by using Webpack's "watch mode". Adding --watch to the Webpackcommand will instruct Webpack to "watch" the source files and recompile wheneverthere is a change. But there's a more powerful option: Webpack'sDevServer

The DevServer is a simple web server that serves content from the distdirectory. Besides recompiling your bundle automatically, it will "live reload"your browser whenever your code recompiles.

Install DevServer:

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

Add the devServer option to our Webpack configuration object(view commit):

// webpack.config.js // ... 'mode': environment, // NEW CODE: // Configuration options for Webpack DevServer, an Express web server that // aids with development and provides live reloading out of the box. devServer: { static: { // The dev server will serve content from this directory. directory: PATH_DIST, }, // Specify a host and port number. host: "localhost", port: 8080, // When using the HTML5 History API (you'll probably do this with React // later), index.html should be served in place of 404 responses. historyApiFallback: true, client: { // Show a full-screen overlay in the browser when there are compiler // errors or warnings. overlay: { errors: true, warnings: true, }, }, }, // END NEW CODE // ...

DevServer doesn't write any files after compiling. It won't write anything todist. It keeps bundle files in memory and serves them as if they were realfiles mounted at the server's root path. For example, <script src="js/main.js">will trigger a request to js/main.js, which will serve the contents of js/main.jsfrom memory.

Add an npm script for the DevServer (view commit):

// package.json{ "scripts": { "build": "webpack --env.environment=production --config webpack.config.js", "dev": "webpack --env.environment=development --config webpack.config.js", "devserver": "webpack-dev-server --env.environment=development --config webpack.config.js" }, // ...

And run it:

npm run devserver

This will fire up a web server on http://localhost:8080. You can visit the URLand check out your browser's development console. You should see the message wewrote with console.log.

Let's recap our three npm scripts:

  • npm run dev will create and emit a development bundle to the dist folder.
  • npm run prod will create and emit a production bundle to the dist folder.
  • npm run devserver will fire up Webpack's DevServer which creates adevelopment bundle, stores it in memory, and serves it. It will also recompileand "live reload" whenever the code changes.

React

We need two packages to use React. First we need thegeneric React package (react). We also need react-dom which takes care ofDOM-specific operationslike rendering our application on the web platform.

npm install --save react react-dom

React components are typically written using JSX, a syntax extensionto JavaScript. We won't learn JSX in this article, but you should know that thiscode:

const element = <h1>Hello, world!</h1>;

is a developer-friendly way of writing:

const element = jsx("h1", null, "Hello, world!");

(Why not React.createElement()? Because we're using the new JSXtransform.)

Browsers don't understand JSX so we need to transform it to calls tojsx(), and import the jsx function.

There's a Babel preset that will do this for us: @babel/preset-react.This preset includes several plugins that are required to write a React app. Ithelps Babel understand the JSX syntax and converts our JSX to jsx() calls.

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

Open webpack.config.js and add @babel/preset-react to the presets arrayunder the rule for babel-loader (view commit):

// ...options: { presets: [ [ "@babel/preset-env", { // ... }, ], // NEW CODE: // The react preset includes plugins that are required for React // apps. For example, it inclues a plugin that transforms JSX. [ "@babel/preset-react", { // Tell "plugin-transform-react-jsx" which runtime to use. // The "automatic" runtime will: // * Import the jsx() function in your JSX files: // `import { jsx as _jsx } from "react";` // * Transform JSX: `<div />` to `_jsx("div")` runtime: "automatic", }, ], // END NEW CODE ];}// ...

Now that Babel is ready to work with JSX, we can create our first React componentand render it to the DOM. Replace the contents of src/index.js with this(view commit):

import React from "react";import ReactDOM from "react-dom";function Root() { return <h1>Hello, world.</h1>;}// Render the Root element into the DOMReactDOM.render(<Root />, document.getElementById("root"));

The call to ReactDOM.render() will render the React component <Root> intothe supplied container. Our container is an HTML element with an ID of root.This element needs to exist in our HTML template, so let's add it to src/index.html:

<!DOCTYPE html><html> <head> <meta charset="utf-8" /> <title>Boilerplate!</title> </head> <body> <div id="root"></div> </body></html>

That's it. Fire up Webpack's DevServer, hit http://localhost:8080 and enjoyyour exciting new app.

Where to go from here

The ecosystem is vast and your boilerplate will likely grow in complexity as youcustomize it further and add more plugins and presets. Maintaining your ownboilerplate gives you a lot of flexibility, but it can also become frustratingto keep up with all the tools. If you don't think it's worth the effort, there'salways create-react-app.

If you want to flesh out your boilerplate code, here are some tips on whatto explore next:

(Video) #3 Set Up React App using Webpack | Webpack Dev Server | Babel | Create React App

  • Install the React DevTools.
  • Set up Hot Module Replacement
  • Tooling for CSS. Either a CSS-in-JS solution like Emotion, or acombination of style-loader, css-loader and a pre-processor loader likesass-loader if you want to keep your CSS away from your JS.
  • Check out copy-webpack-plugin.Put public files like favicons in a dedicated directory (e.g. src/public)and use this plugin to copy the directory's contents to dist.
  • Are you using PropTypes?Install babel-plugin-transform-react-remove-prop-typesif you want to remove them in production.

A future article will likely cover some of these topics.

Read part two: Create a React app from scratch - ESLint and Prettier.

FAQs

Does create React app use webpack and Babel? ›

Create React App

Under the hood, it uses Babel and webpack, but you don't need to know anything about them. When you're ready to deploy to production, running npm run build will create an optimized build of your app in the build folder. You can learn more about Create React App from its README and the User Guide.

Can I use webpack with create React app? ›

Create React App is a great way to get started with a Webpack-React app using standard conventions. Therefore, we'll use it in all of our forthcoming Webpack-React apps.

Does React use Babel or webpack? ›

Frontend: we use Webpack (which uses Babel and other things) to compile JS code and many other assets into a few small bundle files that our users can download when they first load our webpage. For example, create-react-app uses Webpack and Babel when creating your app.

Do I need Babel with create React app? ›

Creating a React application requires you to set up build tools such as Babel and Webpack. These build tools are required because React's JSX syntax is a language that the browser doesn't understand.

Do I need Babel if I use webpack? ›

We need webpack to bundle our code and webpack-cli is a command-line tool that uses webpack to do the same. Also webpack requires babel-loader to transpile our ES6 code to ES5 before bundling (Remember, what I said about being responsible developers 😃).

How do I setup React project with webpack and Babel? ›

Complete React app setup with webpack, babel, and eslint
  1. React app setup. Index.
  2. Step 1: Creating an git repo.
  3. Step 2: Initializing a basic web project.
  4. Step 3 : Adding npm.
  5. Step 4: Adding babel.
  6. Step 5: Adding webpack. And why webpack or what is webpack ? ...
  7. Step 6: Adding eslint and prettier.
Mar 28, 2022

Why webpack instead of create React app? ›

Configuring webpack provides complete control over the development environment, while initializing with Create React App prevents any custom configuration by default.

What is the difference between Babel and webpack? ›

In the preceding section, you learned that webpack is a tool that brings together many loaders and plugins to create a single bundled code file. Babel is a library that does a single job: it compiles JavaScript. Babel is one of many loaders you can use with webpack.

How do I use Babel with webpack? ›

Adding Babel to Webpack
  1. Add dependencies. Let's install babel dependencies: ...
  2. Add babel-loader to Webpack config. You will need to add the following to webpack.config.js. ...
  3. Add HTML elements. ...
  4. Run the App.
Aug 28, 2021

Do we still need Babel in 2022? ›

Conclusion. Having Babel and TypeScript do part of the source transform seems unnecessarily complicated. So, if you are using both, it is better to use Babel for transpiling and the TypeScript compiler for type checking.

Does React still use Babel? ›

Babel's use is not only rooted in React. Its main application is as a compiler to convert code written in ECMAScript2015+ into backwards-compatible JavaScript.

What is the best way to create a React app? ›

The best way to create a React app is by using the toolchain recommended by the React team. The most commonly used way is by using the toolchain create React app. By far it is the most popular and easiest way of creating a React app in just a few minutes.

What is the best backend for React app? ›

js is the best backend with React, as you can use it to create microservices and query builders to solve all database queries. The combination of React for the front end and Node for the back end is perfect for developing a web application that will handle multiple simultaneous requests.

Do you need webpack with create React app? ›

Create-React-App is a great tool to bootstrap React apps, but it offers only limited access to the configuration of the production build. While it uses Webpack under the hood, the WebPack configuration is not exposed to the user - unless you decide to eject .

What is the best architecture for React? ›

React Architecture Best Practices
  • Avoid using an excessive number of nested files and directories, and don't overthink on the application structure.
  • Don't move files around; you can't afford to change file locations when a team of developers is working on the same project.
Jan 30, 2023

What is better than webpack? ›

We've looked at three powerful alternatives for your JavaScript workflow: Vite, Parcel and Esbuild. All of these tools can provide big improvements over Webpack to your JavaScript development flow. If you haven't updated your JavaScript tooling for a while, now is a great time to do so!

What can I use instead of Babel? ›

Webpack, TypeScript, CoffeeScript, ESLint, and rollup are the most popular alternatives and competitors to Babel.

Is webpack still used? ›

While Webpack is still the default JavaScript build tool for many popular applications like Next.

Does React app need webpack? ›

Create-React-App is a great tool to bootstrap React apps, but it offers only limited access to the configuration of the production build. While it uses Webpack under the hood, the WebPack configuration is not exposed to the user - unless you decide to eject .

What is the difference between create React app and webpack? ›

Configuring webpack provides complete control over the development environment, while initializing with Create React App prevents any custom configuration by default.

What version of webpack does create React app use? ›

react-scripts is using webpack version "4.44.

What module bundler does create React app use? ›

Most React apps will have their files “bundled” using tools like Webpack, Rollup or Browserify. Bundling is the process of following imported files and merging them into a single file: a “bundle”. This bundle can then be included on a webpage to load an entire app at once.

Videos

1. React, Webpack, TypeScript, and Babel Environment from Scratch Without Using Create React App CLI
(Full Stack Coder)
2. Setting Up a React App From Scratch (Webpack, Babel, Express)
(Dannon Gilbert)
3. Creating A React App With Webpack & Babel From Scratch -Part 1
(makeThemTech)
4. Create a Modern React App From Scratch | Webpack 5 | 2021
(CyberWolves)
5. React & Webpack 4 From Scratch - No CLI
(Traversy Media)
6. Setting up React from scratch with Webpack Babel and Express
(Fredrik Christenson)
Top Articles
Latest Posts
Article information

Author: Horacio Brakus JD

Last Updated: 12/19/2022

Views: 6386

Rating: 4 / 5 (51 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Horacio Brakus JD

Birthday: 1999-08-21

Address: Apt. 524 43384 Minnie Prairie, South Edda, MA 62804

Phone: +5931039998219

Job: Sales Strategist

Hobby: Sculling, Kitesurfing, Orienteering, Painting, Computer programming, Creative writing, Scuba diving

Introduction: My name is Horacio Brakus JD, I am a lively, splendid, jolly, vivacious, vast, cheerful, agreeable person who loves writing and wants to share my knowledge and understanding with you.