# Webpack
Webpack starts at an entry module, follows imports to build a dependency graph, and emits assets for the browser. A project may contain many source files while producing one or more bundles; a bundle is not necessarily the whole application in a single file.
index.ts → imported TypeScript modules → ts-loader → Webpack → dist/bundle.js
src/index.html → HtmlWebpackPlugin → dist/index.html referencing the bundle
browser → requests the generated HTML and JavaScript
TypeScript checks/transforms code; Webpack connects modules and assets. Neither is the API server. The browser executes the emitted JavaScript, not webpack.config.cjs.
# A minimal TypeScript build
These examples use Webpack 5, webpack-cli 5, ts-loader 9, HtmlWebpackPlugin 5 and TypeScript 5.x in a separate example application. They are development dependencies. Use a supported Node version compatible with those packages; they do not need to become dependencies of a server merely to serve the resulting files.
Prepare src/Student.ts, src/index.ts and tsconfig.json as shown in Types and contracts and Compiler and DOM. Add the following webpack.config.cjs; using .cjs makes the configuration's CommonJS format explicit even when application modules use import.
const path = require('node:path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.ts',
devtool: 'source-map',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: { extensions: ['.ts', '.js'] },
plugins: [
new HtmlWebpackPlugin({ template: './src/index.html' })
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
A minimal src/index.html template:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Students</title>
</head>
<body>
<main id="root"></main>
</body>
</html>
HtmlWebpackPlugin generates HTML that loads the emitted bundle. Do not also hardcode a duplicate bundle script into the template. Its default deferred script loading lets the HTML be parsed before the application accesses #root.
# Loaders versus plugins
| Setting | Responsibility |
|---|---|
entry | Starting point of the dependency graph |
module.rules / loaders | Transform matching module sources, such as TypeScript or CSS |
resolve.extensions | Resolve extensionless imports such as ./Student |
plugins | Extend compilation and asset generation, such as generating HTML |
output | Names and location of emitted assets |
mode | Enables defaults suited to development or production |
Default ts-loader uses the TypeScript compiler and performs type checking. With transpileOnly: true, a separate check such as tsc --noEmit is necessary; Webpack itself is not a type checker. A successful transpilation is not proof of valid types or correct behavior.
# Build once, then serve assets
With the example's dependencies installed, use this script in its package.json:
{
"scripts": {
"build": "webpack --config webpack.config.cjs --mode production"
}
}
npm run build finishes after emitting files. Deploy the generated assets using a static-file server or static-serving middleware; do not run a development server as the production deployment. Source maps are useful for debugging, but decide deliberately whether to publish them with production assets.
Try it: change Student.greeting(), rebuild, and inspect the generated HTML. Which tool removes type annotations, and which tool inserts the script reference?
Next: Development workflow. References: Webpack concepts (opens new window) · TypeScript guide (opens new window).