Step-by-step guide to setting up React and TypeScript with Webpack

  • webpack
    webpack
  • babel
    babel
  • react
    react
  • typescript
    typescript
Published on 2025/01/05

Introduction

Developing modern web applications using React and TypeScript is very popular because it allows you to write efficient and type-safe code. However, setting up the development environment can sometimes be a bit of work. In particular, configuring Webpack to bundle React and TypeScript may feel like a high hurdle for first-time users.

In this article, we will explain step by step how to bundle React and TypeScript with Webpack. We will cover everything you need to actually start development. If you are about to start a project using React and TypeScript, or if you are struggling with Webpack configuration, this content should be helpful, so please read through to the end.

For the basic Webpack configuration, I have explained it in another article, so please refer to that as well.

https;//shinagawa-web.com/blogs/webpack-basics-guide

Goal for this tutorial

We will implement a React component in JSX format, bundle it with Webpack, and make it viewable in the browser.

Image from Gyazo

After that, we will enable bundling TypeScript and then convert the React component to TSX format. Once modified, we will bundle it with Webpack and check that it is displayed in the browser in the same way.

Image from Gyazo

I hope this tutorial helps you picture how to implement React with type-safe development using TypeScript.

Bundling React (JSX)

We need to transform non-standard JavaScript syntax like JSX into plain JS, and Babel is what handles that.

In the previous article, I introduced Babel as a tool to convert newer JavaScript syntax into older syntax. This time, we will use it to convert JSX -> JS.

Babel official site

https://babeljs.io/

Adding a Babel preset

npm i -D @babel/preset-react

We already created a .babelrc file in the project root, so we will specify the preset we just added.

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

Installing React in the project

Install React so that we can use JSX syntax.

npm i react react-dom

Creating the React app

Delete the JS files we have created so far and create a new React app.

rm dist/*.js* src/*.js

Create the App component

src/App.jsx
import React from "react";

export const App = ({name}) => {
  return (
    <h1>{`Hello ${name}`}</h1>
  );
};

We take name as a prop and display it.

src/index.jsx
import { createRoot } from 'react-dom/client';
import React, { StrictMode } from 'react';
import { App } from './App';
import './styles.css'

const root = createRoot(document.getElementById('app'));
root.render(
  <StrictMode>
    <App name={'Test'} />
  </StrictMode>
);

We define it so that the React app is rendered into the element with id='app'.

dist/index.html
  <head>
    <meta charset="utf-8" />
    <title>webpack tutorial</title>
  </head>
  <body>
+     <div id="app"></div>
    <script src="./main.js"></script>
  </body>

We added a new element with id='app' to the HTML where the app will be rendered.

Checking it works

Let’s try bundling with webpack.

npx webpack --mode development

An error occurs.

Image from Gyazo

Up to now, Webpack has been bundling modules by reading src/index.js as the default entry file, but since src/index.jsx is now the entry point, it resulted in an error.

Let’s fix webpack.config.js.

webpack.config.js
/** @type {import('webpack').Configuration} */
module.exports = {
  devtool: "source-map",
  module: {
    rules: [
      {
-         test: /\.js$/,
+         test: /\.jsx?$/,
        loader: "babel-loader",
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
+   resolve: {
+     extensions: ['.js', '.jsx']
+   }
};

If you bundle again, it should succeed this time.

Image from Gyazo

Bundling TypeScript

Now that we can bundle React, next we will bundle code written in TypeScript.

Installing TypeScript

First, install TypeScript.

npm i -D typescript

Also install the type definitions for the functions and objects included in react-dom.

npm i -D @types/react-dom

Creating tsconfig

Create a tsconfig.json file to manage the compiler settings for the TypeScript project.

npx tsc --init

Since we are using React, add "jsx": "react-jsx".

tsconfig.json
{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "jsx": "react-jsx"
  }
}

Installing a loader for TypeScript

Install a loader to read code written in TypeScript.

npm i -D ts-loader

Changing the webpack.config.js settings

Change from babel-loader to ts-loader.

Also update the settings to newly support .ts and .tsx.

webpack.config.js
/** @type {import('webpack').Configuration} */
module.exports = {
  devtool: "source-map",
  module: {
    rules: [
      {
-         test: /\.jsx?$/,
+         test: /\.tsx?$/,
-         loader: "babel-loader",
+         loader: "ts-loader",
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  resolve: {
-     extensions: ['.js', '.jsx']
+     extensions: ['.js', '.jsx', '.ts', '.tsx']
  }
};

Rewriting jsx -> tsx

Change the file extensions and rewrite the code in TypeScript.

src/App.tsx
- import React from "react";

- export const App = ({name}) => {
+ export const App = ({name}: {name: string}) => {
  return (
    <h1>{`Hello ${name}`}</h1>
  );
};

We define the type of name.
Also, since we set "jsx": "react-jsx" in tsconfig.json, import React from "react"; is no longer needed.

src/index.tsx
import { createRoot } from 'react-dom/client';
- import React, { StrictMode } from 'react';
+ import { StrictMode } from 'react';
import { App } from './App';
import './styles.css'

+ const container = document.getElementById('app');
+ if (!container) {
+   throw new Error("Failed to find the root element. Make sure there's an element with id='app' in your HTML.");
+ }

- const root = createRoot(document.getElementById('app'));
+ const root = createRoot(container);

root.render(
  <StrictMode>
-     <App name={'Test'} />
+     <App name={'TypeScript'} />
  </StrictMode>
);

We added handling for the case where the element with id='app' cannot be obtained.
For verification, we changed the text from Test to TypeScript.

Checking it works

Let’s try bundling with webpack.

npx webpack --mode development

We have now confirmed it works in an environment with TypeScript + React.

Image from Gyazo

Writing webpack.config in TypeScript

You can also change webpack.config.js to webpack.config.ts.

(Whether you need to go that far will depend on each project.)

Since webpack.config is read before Webpack bundles, instead of ts-loader we will use ts-node so that Node.js can execute TypeScript scripts as-is.

We also need the Node.js type definition files for TypeScript at runtime, so we will install those as well.

npm i -D ts-node @types/node

Change the extension from webpack.config.js -> webpack.config.ts and slightly adjust the syntax.

webpack.config.ts
import { Configuration } from "webpack";

const config: Configuration = {
  devtool: "source-map",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader",
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx"],
  },
};

export default config;

By setting Webpack’s Configuration type, you can benefit from input completion.

Check that you can still bundle with Webpack as before.

npx webpack --mode development

Bundling HTML

Up to now, we have placed the HTML file in the dist folder and used getElementById to render the React app.
With Webpack, you can also bundle HTML and output everything together.

Installing the plugin

Add html-webpack-plugin to the project.

npm i -D html-webpack-plugin

Add settings to webpack.config.ts

webpack.config.ts
import { Configuration } from "webpack";
+ import HtmlWebpackPlugin from "html-webpack-plugin";

const config: Configuration = {
  devtool: "source-map",
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader",
      },
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx"],
  },
+   plugins: [
+     new HtmlWebpackPlugin({
+       template: "./src/index.html",
+       inject: "body",
+     }),
+   ],
};

export default config;

We added the plugin and defined where the HTML file will be located.
We also configured it to load JavaScript in the body tag.

Next, move the HTML file from dist -> src and remove the script tag.

dist/index.html
  <head>
    <meta charset="utf-8" />
    <title>webpack tutorial</title>
  </head>
  <body>
    <div id="app"></div>
-     <script src="./main.js"></script>
  </body>

Checking it works

Check that you can still bundle with Webpack as before.

npx webpack --mode development

If it displays correctly in the browser, you’re good.

Image from Gyazo

Now that we no longer need to manage the dist folder, let’s add it to .gitignore.

dist/

Conclusion

In this article, we explained how to bundle a React and TypeScript application with Webpack. We walked through the necessary steps one by one, from editing the Webpack configuration file, introducing Babel and TypeScript, all the way to bundling HTML.

Developing with React and TypeScript may have a bit of a learning curve at first, but with the right tools and configuration, your development efficiency can improve significantly. Use this guide as a reference to set up your own development environment and enjoy efficient React app development.

We will continue to provide information useful for web development, so it would be great if you keep checking back.

For the basic Webpack configuration, I have explained it in another article, so please refer to that as well.

https;//shinagawa-web.com/blogs/webpack-basics-guide

Xでシェア
Facebookでシェア
LinkedInでシェア

Questions about this article 📝

If you have any questions or feedback about the content, please feel free to contact us.
Go to inquiry form