Tackling TypeScript
Please support this book: buy it or donate
(Ad, please don’t block.)

9 Creating web apps via TypeScript and webpack

This chapter describes how to create web apps via TypeScript and webpack. We will only be using the DOM API, not a particular frontend framework.

  GitHub repository: ts-demo-webpack

The repository ts-demo-webpack that we are working with in this chapter, can be downloaded from GitHub.

9.1 Required knowledge

You should be roughly familiar with:

9.2 Limitations

In this chapter, we stick with what is best supported by TypeScript: CommonJS modules, bundled as script files.

9.3 The repository ts-demo-webpack

This is how the repository ts-demo-webpack is structured:

  build/   (created on demand)

The web app is built as follows:

Both output tasks are handled by webpack:

9.4 package.json

package.json contains metadata for the project:

  "private": true,
  "scripts": {
    "tsc": "tsc",
    "tscw": "tsc --watch",
    "wp": "webpack",
    "wpw": "webpack --watch",
    "serve": "http-server build"
  "dependencies": {
    "@types/lodash": "···",
    "copy-webpack-plugin": "···",
    "http-server": "···",
    "lodash": "···",
    "ts-loader": "···",
    "typescript": "···",
    "webpack": "···",
    "webpack-cli": "···"

The properties work as follows:

9.5 webpack.config.js

This is how we configure webpack:

const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  entry: {
    main: "./ts/src/main.ts",
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: "[name]-bundle.js",
  resolve: {
    // Add ".ts" and ".tsx" as resolvable extensions.
    extensions: [".ts", ".tsx", ".js"],
  module: {
    rules: [
      // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
      { test: /\.tsx?$/, loader: "ts-loader" },
  plugins: [
    new CopyWebpackPlugin([
        from: './html',


For more information on configuring webpack, see the webpack website.

9.6 tsconfig.json

This file configures the TypeScript compiler:

  "compilerOptions": {
    "rootDir": "ts",
    "outDir": "dist",
    "target": "es2019",
    "lib": [
    "module": "commonjs",
    "esModuleInterop": true,
    "strict": true,
    "sourceMap": true

The option outDir is not needed if we use webpack with ts-loader. However, we’ll need it if we use webpack without a loader (as explained later in this chapter).

9.7 index.html

This is the HTML page of the web app:

<!doctype html>
  <meta charset="UTF-8">
  <div id="output"></div>
  <script src="main-bundle.js"></script>

The <div> with the ID "output" is where the web app displays its output. main-bundle.js contains the bundled code.

9.8 main.ts

This is the TypeScript code of the web app:

import template from 'lodash/template';

const outputElement = document.getElementById('output');
if (outputElement) {
  const compiled = template(`
    <h1><%- heading %></h1>
    Current date and time: <%- dateTimeString %>
  outputElement.innerHTML = compiled({
    heading: 'ts-demo-webpack',
    dateTimeString: new Date().toISOString(),

9.9 Installing, building and running the web app

First we need to install all npm packages that our web app depends on:

npm install

Then we need to run webpack (which was installed during the previous step) via a script in package.json:

npm run wpw

From now on, webpack watches the files in the repository for changes and rebuilds the web app whenever it detects any.

In a different command line, we can now start a web server that serves the contents of build/ on localhost:

npm run serve

If we go to the URL printed out by the web server, we can see the web app in action.

Note that simple reloading may not be enough to see the results after changes – due to caching. You may have to force-reload by pressing shift when reloading.

9.9.1 Building in Visual Studio Code

Instead of building from a command line, we can also do that from within Visual Studio Code, via a so-called build task:

We can now start webpack via “Run Build Task…” from the “Terminal” menu.

9.10 Using webpack without a loader: webpack-no-loader.config.js

Instead of using on ts-loader, we can also first compile our TypeScript files to JavaScript files and then bundle those via webpack. How the first of those two steps works, is described in the previous chapter.

We now don’t have to configure ts-loader and our webpack configuration file is simpler:

const path = require('path');

module.exports = {
  entry: {
    main: "./dist/src/main.js",
  output: {
    path: path.join(__dirname, 'build'),
    filename: '[name]-bundle.js',
  plugins: [
    new CopyWebpackPlugin([
        from: './html',

Note that entry.main is different. In the other config file, it is:


Why would we want to produce intermediate files before bundling them? One benefit is that we can use Node.js to run unit tests for some of the TypeScript code.