CosmicCode

Setting up a basic Webpack 4 React app with Typescript

Created May 25th, 2018

Last updated May 25th, 2018

TypeScript
React
Webpack

Quick start-up guide on how to put together a basic development environment for a React application written in TypeScript and build with Webpack.

While there are really good out of the box kits out there that will get you up and running with React and Typescript (eg: create-react-app with react-scripts-ts) I feel that you can miss out on what is actually going on under the hood. This guide will hopefully give you a better idea of how you can set up your own project with the core tools and utilities - giving you more control over what you include and how you configure it for your project.

The basic structure of the project looks like this. Refer to this when files are mentioned below. Feel free to create the directories now.

.
├── dist
│   ├── bundle.js
│   └── index.html
├── package.json
├── package-lock.json
├── src
│   └── index.tsx
├── tsconfig.json
└── webpack.config.js

Webpack

npm i webpack webpack-cli --save-dev

  • Install react react-dom

npm i react react-dom --save-dev

  • Customize config

Next we create a basic webpack.config.js file in the project root directory. Here we will set mode: 'development' (we will come back to this later)

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/index.tsx',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

We set the entry point to index.tsx this will be the root of the React TypeScript application and is the first thing Webpack will process when building dependencies.

Install typescript

npm i typescript --save-dev

Now that TypeScript compiler is installed we need to create some basic config in the project root directory.

{
  "compilerOptions": {
    "sourceMap": true,
    "jsx": "react" //we need this for react
  }
}

We will be using Webpack as our build tool so we need to tell it to use TypeScript. For this we will use ts-loader, install

npm i ts-loader --save-dev

and configure by adding the following to your webpack.config.js,

  resolve: {
    extensions: [".ts", ".tsx", ".js"]
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: "ts-loader" }
    ]
  }

The resolve section is telling Webpack to process any .ts,tsx or js files while within the module section we have a rule that will tell Webpack to use ts-loader for any .ts or .tsx files via a regular expression.

Configure build script - add the following to package.json

  "scripts": {
    "build": "webpack"
  },

Build

Before we build our application we need to create a test index.tsx file. Let's start with:

import * as React from 'react'
import * as ReactDOM from 'react-dom'

ReactDOM.render(
  <div>It's alive!</div>,
  document.getElementById('root') as HTMLElement
)

We should also create a basic index.html file to display our page. Put this in the dist directory

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>Webpack React Typescript app</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <script src="bundle.js"></script>
    <div id="root"></div>
  </body>
</html>

We should now be able to build our index.tsx with the power of Webpack + TypeScript by running npm run build. You should get output similar to the below:

 Hash: 6d4f525587bc81d87a3d
Version: webpack 4.8.3
Time: 6605ms
Built at: 2018-05-25 18:30:48
    Asset     Size  Chunks             Chunk Names
bundle.js  705 KiB    main  [emitted]  main
Entrypoint main = bundle.js
[./src/index.tsx] 200 bytes {main} [built]
    + 21 hidden modules

We need to serve the webpage to actually see it. To do this we will use webpack-dev-server which also provides live reloading - any changes that are made will trigger a rebuild and a refresh of the page.

npm i webpack-dev-server --save-dev

This will link in with the webpack-cli and serve the output. Let's set it as the start script by adding the following to the scripts section of package.json:

"start": "webpack-dev-server",

We need to configure webpack-dev-server to point at the dist directory. Add this to your webpack.config.js

  devServer: {
    contentBase: path.resolve(__dirname, "dist"),
  }

Now just run npm start and it should open a page in your default browser - you should now see It's alive! printed.

That is about it for getting a basic development environment set up. One last thing that we should do is separate development vs production configuration for Webpack. This is done by having 3 Webpack config files instead of one: webpack.common.js, webpack.dev.js and webpack.prod.js. Shared config goes in common while dev and prod are for development and production respectively.

There is a nice tool called webpack-merge that we will need to install that will allow Webpack to combine (or 'merge') the common configuration into the environment specific config.

npm i webpack-merge --save-dev

We can now remove webpack.config.js and replace with the following

webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  devServer: {
    contentBase: path.resolve(__dirname, "dist") 
  }
});

webpack.common.js

const path = require('path');

module.exports = {
  entry: './src/index.tsx',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"]
  },
  module: {
    rules: [
      { test: /\.tsx?$/, loader: "ts-loader" }
    ]
  },
};

webpack.prod.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const path = require('path');

module.exports = merge(common, {
  mode: 'production',
});

All we need to do now is update the package.json NPM scripts to have webpack-cli point to the updated config:

  "scripts": {
    "start": "webpack-dev-server --open --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js",
  }

That should be it, you can now develop and build your application. This is just the start though, there are many more config options you can tweak. Take a look at the Webpack Configuration documentation

Andrew

Web development enthusiast with a keen interest in anything frontend.