Step-by-step guide to setting up React and TypeScript with Webpack
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.
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.
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
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
import React from "react";
export const App = ({name}) => {
return (
<h1>{`Hello ${name}`}</h1>
);
};
We take name as a prop and display it.
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'.
<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.
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.
/** @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.
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".
{
"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.
/** @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.
- 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.
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.
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.
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
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.
<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.
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.
Questions about this article 📝
If you have any questions or feedback about the content, please feel free to contact us.Go to inquiry form
Related Articles
Practical Component Design Guide with React × Tailwind CSS × Emotion: The Optimal Approach to Design Systems, State Management, and Reusability
2024/11/22How to Easily Build a Web API with Express and MongoDB [TypeScript Compatible]
2024/12/09Express (+ TypeScript) Beginner’s Guide: How to Quickly Build Web Applications
2024/12/07Complete Guide to Refactoring React: Improve Your Code with Modularization, Render Optimization, and Design Patterns
2025/01/13Test Automation with Jest and TypeScript: A Complete Guide from Basic Setup to Writing Type-Safe Tests
2023/09/13ESLint / Prettier Introduction Guide: Thorough Explanation from Husky, CI/CD Integration, to Visualizing Code Quality
2024/02/12Building a Mock Server for Frontend Development: A Practical Guide Using @graphql-tools/mock and Faker
2024/12/30Streamlining API Mocking and Testing with Mock Service Worker (MSW)
2023/09/25



