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 will support 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 of the library. Modern build tools, such as webpack and rollup, will also parse dependencies and eliminate unused (dead) code, if the dependency is an ES module.

Developers working in commonjs, amd or web browser environments will use the umd package.

Why Webpack

Unfortunately, the TypeScript compiler does not support global fallbacks when compiling to umd. In short, umd packages produced by TypeScript will 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 will output type definition files which, in turn, provides code intellisense support to TypeScript developers using the library.
  • files specifies which files for tsc to compile. In this case, the main entry point to the library is listed. Compiling the main entry point, index.ts, will compile the rest of the library through dependencies.


Webpack will bundle the ES modules outputted by tsc as a single umd package targeting ES5. The umd package can be loaded directly within web browsers via a script tag or within commonjs (Node) 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. Webpack is being used to transpile ES2015 to ES5 and not for compiling 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 will generate a umd package that supports both commonjs and web browser environments.
  • module.rules: Webpack will use 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 will 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 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 runs prior to every time npm run build is executed.
  • devDependencies lists the minimal set of packages needed to build both ES2015 modules and a umd package from the TypeScript source code. 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('libraryName');


ES2015 environments

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


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


import { sayHello } from 'package';