Building and publishing a module with TypeScript and Rollup.js

Gerard O'Neill
Jul 14, 2018 · 6 min read

I recently decided to publish an NPM module. I wrote it in TypeScript and went with Rollup for bundling. It was my first time publishing a module and my first experience with Rollup, so I’d like to write a tutorial on how to do it, both as a reference for myself and for anyone who may find this useful. Let’s get started!

Why TypeScript and Rollup

I went with TypeScript for several reasons. It’s what I use at work, so I’m not only familiar with it, but it’s always good to hone my skills, especially since I’m not necessarily a TypeScript expert (yet). I’m also a fan of types for a number of reasons, and compile-time errors are invaluable. I also believe all JS libraries should be authored in TypeScript.

As for Rollup, I mostly went with it because I had never used it and have heard good things about it. Not only that, but I know that a number of open-source projects use it over Webpack. I also read this great post about the differences between the two and why Rollup is better for building libraries.


I’m going to be using yarn, but you can use npm if you wish!

First, let’s create our package.json inside the project directory. Fill out everything as you wish, or just hit Enter at every step if you don’t care yet. You can always edit these later.

$ yarn init

First, let’s install TypeScript and Rollup inside the project directory, as well as a plugin to allow Rollup to compile TypeScript as part of its bundling process.

$ yarn --dev add typescript rollup rollup-plugin-typescript2

Note: The original rollup-plugin-typescript appears to be unmaintained, which is why we’re using this one instead.

At this point, your package.json should look something like this:

"name": "some-project",
"version": "1.0.0",
"main": "index.js",
"author": "John Doe <>",
"license": "MIT",
"devDependencies": {
"rollup": "^0.62.0",
"rollup-plugin-typescript2": "^0.15.1",
"typescript": "^2.9.2"

Let’s add a few extra lines to the file and replace the default for main:

"name": "some-project",
"version": "1.0.0",
"main": "dist/index.js",
"module": "dist/",
"files": ["dist"],
"types": "dist/index.d.ts",
"scripts": {
"build": "rollup -c",
"watch": "rollup -cw"
"author": "John Doe <>",
"license": "MIT",
"devDependencies": {
"rollup": "^0.62.0",
"rollup-plugin-typescript2": "^0.15.1",
"typescript": "^2.9.2"

Here’s what’s going on:

  • main and module point to the bundled JavaScript so that the library consumer may import the module. main is for the CommonJS module, and module is for the ES module. You don’t need to understand the details of that for now, though!
  • files lets npm/yarn know what to publish, or rather what gets installed inside a user’s node_modules when they install your module. For now, let’s just do dist, which will contain our bundled JS.
  • types points to our TypeScript declaration file. This will get compiled automatically for us (more on that below).
  • scripts just has some handy Rollup scripts, which you can use during development. yarn run build will bundle the module once, while yarn run watch will build it every time a file changes.

Now we’re ready for some configuration!

TypeScript configuration

In order to configure TypeScript, we need to create a tsconfig.json file. There are more options than we can go over, but the following ones will be good enough for now. I encourage you to dive deeper into the options if you find yourself unsatisfied with how something is working!

Copy the following into tsconfig.json in the root directory of your project.

"compilerOptions": {
"declaration": true,
"declarationDir": "./dist",
"module": "es6",
"noImplicitAny": true,
"outDir": "./dist",
"target": "es5"
"include": [
"exclude": ["node_modules"]

What’s going on:

  • "declaration": true is there to enable automatic generating of TypeScript’s declaration files. These are helpful for TypeScript users who are using your module, since they’ll have access to all the types of your module’s methods, variables, etc.
  • declarationDir and outDir specify where the compiled code will go. I’m using ./dist, but you may choose another name such as ./lib. If you choose a different directory name, make sure to use that name in your package.json as well!
  • "noImplicitAny": true forces us to be a bit stricter with types. By default, TypeScript allows you to get away with not assigning types to variables whose types cannot be inferred. This option makes it so that if we want something to use the type any, it must be done explicitly.
  • target allows us to specify which version of JavaScript to compile our TypeScript to. In this case, I’m choosing ES5.
  • include allows us to specify where TypeScript should look for .ts, .d.ts, and .tsx files to compile.
  • exclude allows us to specify directories for TypeScript to ignore when it comes to compiling.

Rollup configuration

Next, we’ll configure Rollup.

In the root directory of your project, create a file called rollup.config.js. Next, copy this into there:

import typescript from 'rollup-plugin-typescript2'import pkg from './package.json'export default {
input: 'src/index.ts',
output: [
file: pkg.main,
format: 'cjs',
file: pkg.module,
format: 'es',
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {}),
plugins: [
typescript: require('typescript'),

The first thing to note is that we’re importing package.json. This isn’t necessary, but it’s a handy way to make sure you aren’t duplicating certain information, such as the filename of your bundle. Here’s a breakdown of what’s going on:

  • input tells Rollup where to look for code to bundle. This is similar to Webpack’s entry.
  • output is where our bundle gets stored. As stated earlier, we’re going to bundle both CommonJS (cjs) and ES (es) modules. Rollup’s documentation has a pretty good explanation of the differences if you’re interested.
  • external is what we use to tell Rollup what modules to exclude from our bundle. Since pkg.dependencies will get installed by the module consumer’s yarn or npm, and since pkg.peerDependencies are expected to be installed by the consumer, we can safely exclude those from the bundle.
  • The plugins section is a bit weird. What we’re doing there is making rollup-plugin-typescript2 use the locally-installed TypeScript. By default, it uses a version that likely isn’t up-to-date. There are a bunch of other plugins we could install, but this is fine for now!

Publishing to NPM

In order to publish the module, we first need to log into NPM. If you don’t have an account, go create one now. You’ll only need to do this once on your machine. For some reason, yarn login didn’t work for me, so I’m going to use npm login for this:

$ npm loginUsername: yourusername
Email: (this IS public)
Logged in as yourusername on

Next, let’s create a tiny module to publish. Create a src directory, and inside there, an index.ts file:

// src/index.tsexport const greet = () => console.log('Hello, world!')

After that, run the build command to compile the TypeScript:

$ yarn run buildyarn run v1.3.2
$ rollup -c
src/index.ts → dist/index.js, dist/
created dist/index.js, dist/ in 522ms
✨ Done in 0.91s.

You’ll notice a dist folder was created with three files in it. Feel free to check those out!

Finally, we can publish the module to NPM:

$ yarn publishyarn publish v1.3.2
[1/4] Bumping version...
info Current version: 0.0.1
question New version:
[2/4] Logging in...
[3/4] Publishing...
success Published.
[4/4] Revoking token...
info Not revoking login token, specified via config file.
✨ Done in 1.85s.

And that’s it! You’ve successfully published a module to NPM using TypeScript and Rollup.

To verify that everything looks good, create another directory somewhere else on your computer and run yarn init, then yarn add your-package-name. If you navigate to node_modules/your-package-name, you should see the dist folder with all the compiled files in there! You can then import your module like so:

import { greet } from 'your-package-name'

Developing locally

As you can imagine, it would be pretty annoying and hacky to publish your package every time you wanted to test it out. While writing automated tests helps a lot in this regard, sometimes you’ll need to actually use your module to make sure that it works properly before publishing it.

For this, we can use yarn link (or npm link, of course).

First, navigate to your module’s project directory and run the following:

$ yarn link

Next, navigate to the project where you’d like to consume this module. Make sure that the module is not installed via yarn or npm, then run:

$ yarn link your-package-name

This will create a symbolic link to your package folder inside your project’s node_modules directory. Essentially, you’ll be able to use the local version of your package the same way you’d be able to use it if you had it published and downloaded/installed via yarn add or npm install!

Thanks for reading

If you’ve made it this far, congratulations on publishing something to NPM! I hope you enjoyed this tutorial. If I made any mistakes or if anything needs clarification, please don’t hesitate to reach out. I’d like to contribute more to NPM going forward, so I’ll try to keep this tutorial as up-to-date as I can.


Sign up for Get Better Tech Emails via


how hackers start their afternoons. the real shit is on Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Thanks to Jacky Alciné

Gerard O'Neill

Written by

Just a crazy kid whose thoughts may get him in trouble one day. Nothing I say reflects the opinions of people who pay me.

Elijah McClain, George Floyd, Eric Garner, Breonna Taylor, Ahmaud Arbery, Michael Brown, Oscar Grant, Atatiana Jefferson, Tamir Rice, Bettie Jones, Botham Jean

Gerard O'Neill

Written by

Just a crazy kid whose thoughts may get him in trouble one day. Nothing I say reflects the opinions of people who pay me.

Elijah McClain, George Floyd, Eric Garner, Breonna Taylor, Ahmaud Arbery, Michael Brown, Oscar Grant, Atatiana Jefferson, Tamir Rice, Bettie Jones, Botham Jean

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store