I was following the React & Webpack TypeScript guide but found it to be outdated and broken.
Hence, I documented my own guide in wiring up TypeScript with React and webpack.
Project Layout
First, create a directory for your project:
mkdir react-webpack && cd react-webpack
Install the dependencies (I’m using yarn
but you can use npm
):
yarn add webpack@4 \
webpack-cli \
react \
react-dom \
@types/react \
@types/react-dom \
typescript \
awesome-typescript-loader@5
Note: If you’re using webpack@3
(and not webpack@4
with webpack-cli
), then you’ll need to use awesome-typescript-loader@4
.
The dependencies can be described as follows:
webpack
/webpack-cli
is the bundlerreact
/react-dom
is the library we’re using to build our app@types/react
/@types/react-dom
provides the type definitionstypescript
is the compilerawesome-typescript-loader
is the webpack loader
TypeScript Configuration
Create a TypeScript configuration file:
touch tsconfig.json
And add the following:
{
"compilerOptions": {
"jsx": "react",
"lib": ["dom", "es2015"],
"module": "commonjs",
"noImplicitAny": true,
"outDir": "./dist/",
"sourceMap": true,
"target": "es5"
},
"include": ["./src/**/*"]
}
Options
Let’s go over each option:
compilerOptions
: the compiler optionsjsx: react
: supports JSX in.tsx
fileslib
: includes a list of library files in the compilation (specifyinges2015
allows you to use ES6 syntax)module
: specifies the module code generationnoImplicitAny
: raises errors on expressions and declarations with an impliedany
type (to enable all strict type checking options, usestrict
instead)outDir
: the output directorysourceMap
: generates.map
, which is useful for debuggingtarget
: the target ECMAScript version to transpile down to (pick the version that supports your browser requirements)include
: the files to be included (there’s alsoexclude
)
First Component
Let’s create our first component:
mkdir -p src/App/ && touch src/App/index.tsx
And add the code:
// src/App/index.tsx
import * as React from 'react';
interface Props {
name: string;
}
interface State {}
export default class App extends React.Component<Props, State> {
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
Why are we using import * as React from 'react'
instead of import React from 'react'
?
Because react
uses CommonJS syntax (module.exports = ...
) and not ES Module syntax (export default ...
).
If you use import React from 'react'
, you’ll receive the TypeScript compiler error:
yarn tsc
error TS1192: Module '"react"' has no default export.
Note: However, if you really want to use import React from 'react'
, you can enable allowSyntheticDefaultImports
in the config:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
// ...
}
}
Render App
Let’s create our entry file:
touch src/index.tsx
And add the code:
// src/index.tsx
import * as React from 'react';
import { render } from 'react-dom';
import App from './App';
render(<App name="world" />, document.getElementById('app'));
Then create the HTML file:
mkdir public && touch public/index.html
And add the markup:
<!-- public/index.html -->
<div id="app"></div>
<script src="../dist/bundle.js"></script>
Configure Webpack
Let’s create our webpack configuration file:
touch webpack.config.js
And add the following:
// webpack.config.js
const { resolve } = require('path');
module.exports = {
mode: 'production',
entry: './src/index.tsx',
output: {
filename: 'bundle.js',
path: resolve(__dirname, './dist/'),
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
devtool: 'source-map',
};
On a high-level, this configuration file tells webpack to start at the entry file, resolve every require/import, and then output a bundle.
When webpack sees a file with the extension .ts
or .tsx
, it transpiles the file contents using the typescript loader.
Build Bundle
To build the bundle, run:
yarn webpack
Open App
To open the app in a browser, run:
open public/index.html
Conclusion
You can find the tutorial code here.