How to publish libraries written in TypeScript?

TLDR; Use tsc to compile TypeScript source code to ES modules. Use webpack + babel to transpile ES modules to a umd package. Providing both ES modules and a umd package supports a larger developer community.


A two-step build process.

  1. Compile TypeScript source code to ES modules using the TypeScript compiler, tsc.
  2. Bundle ES modules into a single umd module using webpack and babel.


Providing both ES modules and a umd package gives developers more options for consuming the library. Those working in modern ES2015+ environments can use the ES module version while the umd package supports those working in commonjs or web browser environments. Plus, modern build tools take advantage of ES modules. Tools, such as webpack and rollup, parse and eliminate unused (dead) code of dependencies packaged as ES modules.

Why Webpack

Unfortunately, the TypeScript compiler does not support global fallbacks when compiling to umd. In short, umd packages produced by TypeScript do not support web browsers. Webpack, on the other hand, builds umd packages that support web browsers along with commonjs and amd environments.

Why Babel

To transpile ES2015 to the more widely supported ES5 syntax.

Is Compilation Necessary

What about writing TypeScript libraries for TypeScript developers? Is a build process necessary?

Yes. TypeScript will not compile dependencies (packages listed in node_modules). TypeScript libraries need to be compiled to JavaScript before publishing, even if the intended audience is TypeScript developers.

Project Structure

|-- src/
    |-- sayHello.ts
    |-- index.ts
|-- tsconfig.json
|-- webpack.config.js
|-- package.json


A simple function to say hello! This is the only functionality of this library.

export function sayHello(name: string): void {
    console.log(`Hello, ${name}`);


This is the main entry point to the library and, as such, exposes the public API.

export { sayHello } from "./sayHello";


The following options instruct the TypeScript compiler to output ES2015 modules.

  "compilerOptions": {
    /* Basic Options */
    "target": "ES2015",  /* Build to ES2015 */
    "module": "ES2015",  /* using ES2015 modules */
    "lib": ["es2015", "dom"], /* Using ES2015 features and DOM APIs  */
    "declaration": true, /* Generates corresponding'.d.ts' files. */
    "declarationDir": "./dist/typings/", /* build '.d.ts' files to ./dist/typeings */
    "outDir": "./dist/esm/", /* build to ./dist/esm/ */
  "files": [
  • declarations: set to true, tsc outputs type definition files which, in turn, provides code intellisense support to TypeScript developers using the library.
  • files specifies which files to compile. In this case, the main entry point to the library is listed. Compiling the main entry point, index.ts, compiles the rest of the library through dependencies.


Webpack bundles ES modules outputted by tsc as a single umd package, targeting ES5. Developers can load the umd package within web browsers, via a script tag, or commonjs or amd environments.

const path = require('path');

module.exports = (env) => {

  return {
    entry: {
      'index': path.resolve(__dirname, './dist/esm/index.js')
    output: {
      path: path.resolve(__dirname, './dist/umd'), // builds to ./dist/umd/
      filename: '[name].js', // index.js
      library: 'myLibrary', // aka window.myLibrary
      libraryTarget: 'umd', // supports commonjs, amd and web browsers
      globalObject: 'this'
    module: {
      rules: [
        { test: /\.t|js$/, use: 'babel-loader' }

  • entry specifies the entry point to the library. In this setup, webpack transpiles ES2015 to ES5 and does not compile TypeScript source code. For this reason, the file outputted by tsc, which is an ES2015 module, is specified as the input for webpack.
  • library specifies the global variable name to use within web browsers.
  • globalObject sets the global fallback object. The default is window, which is not defined in commonjs environments. Setting the global fallback object to this generates a umd package that supports both commonjs and web browser environments.
  • module.rules: In this setup, webpack uses babel to transpile ES2015 code to the more widely supported ES5 syntax.


Add the following to package.json

  "main": "./dist/umd/index.js",
  "module": "./dist/esm/index.js",
  "types": "./dist/typings/index.d.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "prebuild": "tsc"
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "typescript": "^2.7.2",
    "webpack": "^4.1.1",
    "webpack-cli": "^2.0.11"

  • main specifies the location of the umd package. Commonjs environments use this as the main entry point to the library.
  • module specifies the location of the main ES2015 module. Consuming packages that use webpack or rollup will use this as the main entry point to the library and will eliminate unused (dead) code when bundling the library.
  • types specifies the location of type definition files. Type definition files provide code intellisense and autocompletion support to TypeScript developers using the library.
  • runs webpack, transpiling ES2015 modules to an ES5 umd package.
  • scripts.prebuild runs tsc, compiling TypeScript to ES2015 modules. This command automatically runs prior to npm run build.
  • devDependencies lists the minimal set of packages needed to build both ES2015 modules and a umd package. Don't forget to run npm install.

Building the library

$ npm run build




Web Browsers via script tags

<!DOCTYPE html>
<html lang="en">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script src="./path/to/dist/umd/index.js"></script>
    // webpack exposes the library as a global, myLibrary

commonjs (Node)

var myLibrary = require('myLibrary');


ES2015 environments

import { sayHello } from 'myLibrary/esm/sayHello';


Developers using webpack do not have to specify ES module paths. Webpack will automatically use the ES modules based on the location specified in package.json.


import { sayHello } from 'myLibrary';