How to Set Up & Deploy Your React App from Scratch Using Webpack and Babel

React continues to dominate the frontend landscape. According to the 2021 State of JS survey, React is used by 80% of respondents, with 64% expressing high satisfaction. A big part of React‘s success is its rich ecosystem of tools and libraries.

Two pillars of the React ecosystem are Webpack and Babel. Webpack is a static module bundler used by over 80% of React developers according to npm trends. Babel is a JavaScript compiler used to convert newer syntax into backwards-compatible code.

So how do Webpack and Babel fit into your React development workflow? And do you really need to set them up manually? Let‘s dive in!

Manual Setup vs create-react-app

If you‘re new to React, you might have used Create React App (CRA) to bootstrap a new project. CRA is an officially supported way to create single-page React apps with no build configuration. You get a pre-configured project with a single command:

npx create-react-app my-app

CRA abstracts away the complex configuration of Webpack, Babel, and other build tools. This allows you to focus on writing React components. However, this simplicity comes with tradeoffs:

  • Limited configuration options without ejecting
  • Unnecessary features and dependencies for some projects
  • Lack of visibility into how the build process works

In contrast, setting up your own build configuration gives you complete control over your project. Some benefits of a manual setup include:

  • Fine-grained control over Webpack and Babel settings
  • Ability to integrate other tools and libraries seamlessly
  • Opportunity to learn how the build process works under-the-hood

So when might you choose a manual setup over CRA? Here are some scenarios:

  • You need to customize the build beyond what CRA allows
  • You‘re building a non-standard React app (not single-page)
  • You want to minimize bundle size by only including necessary dependencies
  • You‘re integrating React into an existing project
  • You want to understand and control your build process

With that context in mind, let‘s walk through setting up a React app from scratch!

Step 1: Project Setup

First, create a new directory and initialize an npm project:

mkdir react-app
cd react-app
npm init -y  

Install React and ReactDOM:

npm install react react-dom

Create a source directory and a minimal React component:

mkdir src
touch src/index.js
// src/index.js
import React from ‘react‘;
import ReactDOM from ‘react-dom‘;

function App() {
  return ;  
}

ReactDOM.render(<App />, document.getElementById(‘root‘));

Step 2: Configuring Webpack

Webpack is a static module bundler for JavaScript applications. At its core, Webpack takes modules with dependencies and generates static assets representing those modules. It recursively builds a dependency graph and emits a bundle that a browser can load.

Some key Webpack concepts:

  • Entry: The entry point is where Webpack starts building the dependency graph. It indicates which module Webpack should use to begin the bundling process.
  • Output: The output property tells Webpack where to emit the bundles it creates and how to name these files.
  • Loaders: Loaders enable Webpack to process files other than JavaScript. They transform files into modules as they are added to the dependency graph.
  • Plugins: Plugins allow you to hook into the entire compilation lifecycle. They can perform a wider range of tasks than loaders, like bundle optimization and asset management.

Let‘s set up a basic Webpack config for our React app.

Install Webpack and related dependencies:

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

Create a webpack.config.js file in the root directory:

// webpack.config.js
const path = require(‘path‘);
const HtmlWebpackPlugin = require(‘html-webpack-plugin‘);

module.exports = {
  mode: process.env.NODE_ENV || ‘development‘,
  entry: ‘./src/index.js‘,
  output: {
    filename: ‘bundle.[hash].js‘,
    path: path.resolve(__dirname, ‘dist‘),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: ‘./public/index.html‘,
    }),
  ],
};

This config tells Webpack to:

  1. Use src/index.js as the entry point
  2. Output the bundled JavaScript to dist/bundle.[hash].js
  3. Generate an HTML file in dist/ using public/index.html as a template

The [hash] in the output filename enables cache busting – generating a unique hash for each build.

Option Description
mode Build mode: "development", "production", or "none"
entry Entry point to start the bundling process
output Instructions for how to write the compiled files to disk
plugins Array of plugins to customize the build process

Table 1: Key Webpack configuration options

Create the template HTML file:

<!-- public/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

Finally, add some scripts to package.json:

"scripts": {
  "start": "webpack serve",
  "build": "NODE_ENV=production webpack"
}

You can now run the development server with:

npm start

And create a production build with:

npm run build

Step 3: Configuring Babel

Babel is a toolchain for compiling JavaScript. It allows you to use the latest JavaScript features by transforming your code into backwards-compatible syntax.

Since React relies heavily on newer features like JSX, classes, and ES modules, Babel is an essential part of the build process. It parses your code, looks for any unsupported syntax, and rewrites it into code that runtimes and browsers support.

Some key concepts:

  • Plugins: Babel Plugins are bite-sized JavaScript transformations. Each plugin transforms a specific syntax feature. For example, babel-plugin-transform-arrow-functions converts arrow functions into regular functions.
  • Presets: Babel Presets are collections of plugins. They bundle together related transformations in a sensible way. The most common presets are @babel/preset-env for compiling modern JavaScript and @babel/preset-react for compiling JSX.

Let‘s configure Babel for our React project.

Install the necessary dependencies:

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

Update webpack.config.js to use babel-loader:

module.exports = {
  ...
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: [‘babel-loader‘],
      },
    ],
  },
  resolve: {
    extensions: [‘*‘, ‘.js‘, ‘.jsx‘],
  },
  ...
};

This tells Webpack to use babel-loader to process all .js and .jsx files, excluding the node_modules directory.

Create a .babelrc config file:

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

This minimal config enables support for modern JavaScript (ES6+) and JSX.

Preset Purpose
@babel/preset-env Smart defaults for compiling modern JavaScript
@babel/preset-react Transforms JSX to React.createElement calls
@babel/preset-typescript Strip out type annotations and compile to target version of JavaScript

Table 2: Common Babel presets for React development

With this setup, you can now use JSX and modern JavaScript features in your React components!

Step 4: Adding Development Tools

Let‘s extend our setup with some quality-of-life improvements and best practices.

Formatting Code with Prettier

Prettier is an opinionated code formatter that ensures consistent code style across your project. It supports many languages, integrates with most editors, and has few configuration options.

Install Prettier:

npm install --save-dev --save-exact prettier

Create a .prettierrc config file:

{
  "singleQuote": true,  
  "trailingComma": "all",
  "printWidth": 100
}

This enforces single quotes, trailing commas, and a print width of 100 characters.

Add a format script to package.json:

"scripts": {
  ...
  "format": "prettier --write \"**/*.+(js|jsx|json|yml|yaml|css|less|scss|ts|tsx|md|gql|graphql|mdx)\""
}

You can now format all supported files with:

npm run format  

Linting with ESLint

ESLint statically analyzes your code to find and fix problems quickly. It‘s highly configurable and has a large ecosystem of plugins.

Install ESLint and related dependencies:

npm install --save-dev eslint eslint-plugin-react eslint-plugin-react-hooks 

Create a .eslintrc config file:

{
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",  
    "plugin:react-hooks/recommended"
  ],
  "plugins": [
    "react",
    "react-hooks" 
  ],
  "parserOptions": {
    "ecmaVersion": 2021,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
}

This config:

  • Extends the recommended configs from ESLint, eslint-plugin-react, and eslint-plugin-react-hooks
  • Enables JSX parsing and browser/node global variables
  • Sets the ECMAScript version to 2021

Add a lint script:

"scripts": {
  ...
  "lint": "eslint \"**/*.+(js|jsx)\"",
}  

You can now lint your code with:

npm run lint

Debugging with Source Maps

Source maps allow you to debug your compiled code as if you were looking at the original source. They map the transformed code back to the original code.

To enable source maps, update your Webpack config:

module.exports = {
  ...
  devtool: ‘inline-source-map‘,
  ...  
};

The inline-source-map setting includes the source map as a DataUrl in the bundle.

Styling with CSS-in-JS

CSS-in-JS libraries allow you to write scoped and dynamic styles in your components. They make it easy to build self-contained and reusable components.

One popular option is styled-components. Install it with:

npm install --save styled-components

You can now define and use styled components:

import React from ‘react‘;
import styled from ‘styled-components‘;

const Button = styled.button`
  background: ${props => props.primary ? "palevioletred" : "white"};
  color: ${props => props.primary ? "white" : "palevioletred"};
`;

function App() {
  return (
    <>
      <Button>Normal</Button>  
      <Button primary>Primary</Button>
    </>
  );
}

Step 5: Deploying to Netlify

Netlify is a popular platform for deploying static sites and frontend applications. It offers continuous deployment, global CDN, free HTTPS, and many other features.

To deploy your React app to Netlify:

  1. Push your code to a Git repository (e.g., GitHub, GitLab)
  2. Create a new site in Netlify and link it to your Git repository
  3. Specify the build command and publish directory
    • Build command: npm run build
    • Publish directory: dist
  4. Click "Deploy site"

Netlify will now build and deploy your app every time you push to the linked branch. You can also enable Deploy Previews to generate previews for pull requests.

Some Netlify-specific best practices:

  • Use a _redirects file for client-side routing
  • Proxy API requests to avoid CORS issues
  • Set cache headers for static assets
  • Use Netlify Forms for serverless form handling
  • Inject environment variables with Netlify‘s build context

Refer to the Netlify Docs for more guides and tutorials.

Troubleshooting Common Issues

Here are some common issues you might encounter and how to resolve them:

"Module not found: Error: Can‘t resolve…"

This error occurs when Webpack can‘t find a module you‘re trying to import. Double check that the file path is correct and the file exists. If you‘re importing a package, make sure it‘s installed.

"Invalid Hook Call Warning"

This warning appears when you violate the Rules of Hooks. Only call Hooks at the top-level of your components or custom Hooks. Don‘t call them inside loops, conditions, or nested functions.

"Unexpected token < in JSON at position 0"

This usually means you have a syntax error in your JSON config file. Validate your JSON with a linter or online validator. Common issues include missing/extra commas and unquoted keys.

Bundle size is too large

To reduce your bundle size, try:

  • Use the production mode in Webpack (mode: ‘production‘)
  • Split your code with dynamic imports and React.lazy
  • Use the TerserPlugin to minify your code
  • Analyze your bundle with the BundleAnalyzerPlugin

Changes aren‘t reflected after editing a file

Make sure your Webpack dev server is set up with hot module replacement (HRM). You can enable it with the hot: true option. Also, check that you‘re actually exporting your React components.

If you‘re still stuck, try searching for your error message on StackOverflow, GitHub issues, or the library‘s documentation. Chances are someone else has encountered the same issue!

Next Steps

Congratulations on making it to the end! You now have a fully-functional React application with a custom Webpack and Babel setup. You can extend this setup even further with:

  • Server-side rendering with libraries like Next.js or Razzle
  • Static type checking with TypeScript or Flow
  • CSS preprocessors like Sass or PostCSS
  • Serverless functions for backend logic
  • Automated testing with Jest and React Testing Library
  • Continuous Integration and Deployment (CI/CD) pipelines

Remember, there‘s no one "right" way to configure a React app. The best setup depends on your specific requirements and constraints. Don‘t be afraid to experiment and iterate!

Here are some resources to dive deeper:

Happy coding!

Similar Posts