Use case

Rollup for College Students: Publish Your First npm Package (Resume-Grade 2026 Guide)

Resume-grade Rollup 4.x tutorial for CS undergrads: real config, 8-step npm publish walkthrough, tree-shaking demo with 87% size reduction, GitHub Actions release pipeline.

23 min read·Updated 2026

If you are a college CS student staring at "build tools" on a job description and wondering whether to learn Webpack, Vite, esbuild, or Rollup, the honest answer is: learn Rollup first, ship a real npm package with it before you graduate, and put the package URL on your resume. Rollup is the JavaScript ES module bundler that Vite uses under the hood for its production builds, it powers most popular open-source libraries (React, Vue, D3, three.js all bundle with Rollup or a fork of it), and a single weekend of work gets you a published, installable npm package you can demo in a phone screen. This guide is the resume-grade walkthrough nobody hands undergrads: real Rollup 4.x configs, a full eight-step npm-publish workflow, a tree-shaking demo with real numbers, a GitHub Actions release pipeline, and a decision matrix for when Rollup beats Vite or esbuild for student work.

Why Rollup belongs on a CS undergrad's resume

Build tooling is the most common gap between "I took a JS class" and "I have shipped software." Recruiters and engineering managers skim resumes for proof you understand how code becomes a deployable artifact: bundlers, module formats, tree-shaking, source maps, and CI/CD. You can stack three of those signals at once by spending one weekend publishing a small library with Rollup.

The pattern that gets interviews is not "I configured Webpack for a coursework project." It is "I published an open-source npm package, bundled with Rollup 4 in ES + CJS + UMD outputs, with tree-shaking verified at 87% size reduction and an automated release pipeline on GitHub Actions." That sentence answers four common phone-screen questions in one breath: what is a module, what is tree-shaking, what is CI, and have you shipped anything outside coursework.

Rollup is also the lowest-friction bundler to learn. Its zero-config command-line works on the first try. Its plugin API is small and well-documented. Its output is human-readable, which means when something breaks you can open the bundle and see exactly what happened. Webpack outputs a runtime-laden mess that takes an hour to read; Rollup outputs JavaScript a sophomore can debug.

There is a longer-term reason to pick Rollup as your first bundler. The JavaScript ecosystem has consolidated around a small number of canonical tools: Vite for app development, esbuild for raw transformation speed, and Rollup for library publishing. Each of those tools has its own quirks, but they share underlying concepts: entry points, output formats, plugin hooks, and tree-shaking. Rollup happens to expose those concepts in the cleanest, smallest-surface way, which is exactly what you want when you are learning. Once you understand a rollup.config.js, reading a vite.config.js is straightforward; many of the same options apply. The skills transfer in both directions.

There is also a credibility dividend. When a senior engineer asks "what do you know about bundlers" in a phone screen, the answer "I have published a package to npm with Rollup, and here is the URL" is dramatically more credible than "I watched a tutorial on Webpack." Recruiters can verify the npm URL in three seconds. Hiring managers can read your rollup.config.js on GitHub before the interview. The proof artifact is the whole point.

What Rollup actually is, in undergrad terms

Rollup is a JavaScript bundler. A bundler takes many small JavaScript files (your code plus the npm packages you imported) and produces one or a few larger files a browser, Node.js, or another build tool can consume.

Rollup was designed around the ECMAScript module standard (the import and export keywords you already use in class). Because ES modules are statically analyzable, Rollup can do tree-shaking: it traces which exports your code actually uses and drops the rest. If you import { debounce } from 'lodash-es' and never use the other 200 lodash functions, they do not end up in your bundle. That is what tree-shaking means, and Rollup invented the practical version of it before Webpack copied it.

Rollup is built for libraries, not full web apps. The mental model is: if you are shipping code other developers will install from npm, use Rollup. If you are shipping a website end users will visit, use Vite (which uses Rollup internally for the production build). For CS coursework where the deliverable is a portfolio package or a utility library, Rollup is the right tool.

It helps to know who actually ships their code through Rollup today, because seeing real projects makes the choice concrete. React's official package set bundles with Rollup. Vue 3 bundles with Rollup. Svelte uses Rollup as its default starter template. D3 ships with Rollup. three.js, Lit, Preact, RxJS, and pretty much every major React component library you have ever installed went through a Rollup config before reaching your node_modules. When you learn Rollup you are not learning a niche tool; you are learning the bundler the open-source library ecosystem standardized on.

It also helps to know what Rollup is not. Rollup is not a dev server. It does not give you hot module replacement out of the box (Vite does, by wrapping Rollup with a dev-time esbuild pipeline). Rollup is not a full asset pipeline; it does not pre-process CSS or images in any opinionated way (plugins exist, but it is not the focus). Rollup is not a transpiler; you bolt Babel or TypeScript onto it via plugins. Rollup is, in one sentence, the part of the toolchain that turns a tree of ES modules into a publishable bundle. Everything else is either upstream (transpilation) or downstream (publish, deploy, consume).

Setup: what you need before you start

You need three things on your laptop. First, Node.js LTS (24.x at time of writing). Install it from nodejs.org or via a version manager like nvm or fnm. Second, a free npm account from npmjs.com (register with your real name because future employers will Google the username). Third, a GitHub account, also real-name, because the repo will live there.

Verify the install:

bash
node --version    # should print v24.x.x or higher
npm --version     # should print 10.x or higher
git --version     # any 2.x is fine

If you have not used npm login before, do it now:

bash
npm login

This stores an auth token in ~/.npmrc. You will need it for the publish step.

Your first Rollup bundle in 90 seconds

Make a project directory and follow along:

bash
mkdir my-first-bundle && cd my-first-bundle
npm init -y
npm install --save-dev rollup

Create src/math.js:

javascript
export function addPi(x) {
  return x + 3.14;
}

export function addE(x) {
  return x + 2.718;
}

export function unused() {
  return "you should never see this string in the bundle";
}

Create src/index.js:

javascript
import { addPi } from './math.js';

console.log('addPi(10) =', addPi(10));

Bundle it:

bash
npx rollup src/index.js --file dist/bundle.js --format es

Open dist/bundle.js. You will see:

javascript
function addPi(x) {
  return x + 3.14;
}

console.log('addPi(10) =', addPi(10));

Notice what is missing: addE, unused, the constant E, and all the import/export keywords. Rollup statically traced which exports index.js actually consumes, included only addPi and its dependencies, and dropped the rest. That is tree-shaking in 90 seconds. If you ran the same experiment with Webpack you would get a 4-kilobyte runtime wrapper around the same logic; Rollup gives you the logic alone.

Try one more variation to lock the idea in. Change src/index.js to also import addE, rebuild, and watch addE reappear in the bundle. Now change it back so only addPi is imported, rebuild, and watch addE disappear again. The bundle truly reflects what is reachable from your entry point. Make a mental note: every Rollup decision flows from the import graph. Add an unused export and it costs you nothing; add an unused import and Rollup tells you (it warns about unused imports in development mode). That feedback loop is one of the reasons libraries authored in Rollup tend to stay lean.

The real Rollup 4.x config every library uses

Zero-config is fine for the first demo. Real libraries need a config file because they publish multiple output formats, run through TypeScript or Babel, and use the canonical plugin stack to consume npm dependencies. Create rollup.config.js at the project root:

javascript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';
import pkg from './package.json' with { type: 'json' };

const external = [
  ...Object.keys(pkg.dependencies || {}),
  ...Object.keys(pkg.peerDependencies || {}),
];

export default {
  input: 'src/index.ts',
  external,
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      sourcemap: true,
      exports: 'named',
    },
    {
      file: pkg.module,
      format: 'es',
      sourcemap: true,
    },
    {
      file: pkg.unpkg,
      format: 'umd',
      name: 'MyLibrary',
      sourcemap: true,
      plugins: [terser()],
    },
  ],
  plugins: [
    resolve({ extensions: ['.ts', '.js'] }),
    commonjs(),
    typescript({ tsconfig: './tsconfig.json' }),
  ],
};

Install the plugins:

bash
npm install --save-dev \
  @rollup/plugin-node-resolve \
  @rollup/plugin-commonjs \
  @rollup/plugin-typescript \
  @rollup/plugin-terser \
  typescript tslib

Two things to learn from this config because they show up in interviews. First, the external array. By default Rollup tries to inline every dependency. For a library you almost never want that; you want users to install your dependencies themselves so multiple libraries can share one copy. Marking dependencies as external is what separates an amateur npm package from a professional one. Second, the three outputs. CommonJS for old Node.js consumers, ES modules for modern bundlers (so they can tree-shake your code), UMD for the rare <script> tag user. Real libraries publish all three.

A short note on each plugin so you can defend the choices in an interview. @rollup/plugin-node-resolve is what lets Rollup follow imports into node_modules. Without it, import lodash from 'lodash' is a fatal error because Rollup does not know the Node resolution algorithm by default. @rollup/plugin-commonjs translates any CommonJS dependencies (most legacy npm packages) into ES modules on the fly so Rollup can tree-shake them. @rollup/plugin-typescript runs the TypeScript compiler as a Rollup pass, which means you get type checking, declaration files, and JS output in one build. @rollup/plugin-terser minifies output and is the modern Rollup-maintained replacement for the older rollup-plugin-terser. The combination of those four plugins is the canonical baseline for a 2026 library config; if you copy it verbatim into your first real project, the only thing you typically change is the input path and the UMD name global.

The npm publish walkthrough: eight steps to your first release

This is the resume-grade workflow. By the end you have a public, installable npm package and a GitHub repo to link.

Step 1: Pick a package name and scope

Use a scoped name so you do not collide with existing packages: @yourname/utility-name. For example @jane-doe/sleep-promise. Scopes are free on npm and they look more professional than random-utility-2837. Edit package.json:

json
{
  "name": "@jane-doe/sleep-promise",
  "version": "0.1.0",
  "description": "A tiny Promise-based sleep utility for async/await code.",
  "license": "MIT",
  "author": "Jane Doe <[email protected]>",
  "repository": "github:jane-doe/sleep-promise"
}

Step 2: Set the entry points correctly

This is where most first-time publishers get it wrong. Add to package.json:

json
{
  "main": "dist/index.cjs.js",
  "module": "dist/index.esm.js",
  "unpkg": "dist/index.umd.min.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.esm.js",
      "require": "./dist/index.cjs.js"
    }
  },
  "files": ["dist", "README.md", "LICENSE"]
}

The exports field is what modern bundlers and Node.js read first. The main/module/unpkg fields are fallbacks for old tools. The files array tells npm what to actually publish; without it you accidentally ship your src folder, tests, .git, and your .env. Always set files.

Step 3: Write a real README

The README is what shows up on npmjs.com and is the first thing a hiring manager reads. Structure it as:

  1. One-sentence description.
  2. Install command: npm install @jane-doe/sleep-promise.
  3. A usage example a sophomore can copy-paste.
  4. The API table.
  5. A "Why this exists" paragraph.

Skip the badges if you do not have CI green yet. Empty badge rows make a package look abandoned.

Step 4: Add a LICENSE file

MIT is the right choice unless your school requires otherwise. Copy the canonical MIT text from choosealicense.com, paste it into a LICENSE file at the repo root, fill in the year and your name. This single file is the difference between "open-source library" and "random code dump."

Step 5: Build and inspect

bash
npx rollup -c
ls -la dist/

You should see index.cjs.js, index.esm.js, index.umd.min.js, source map files, and index.d.ts if you used TypeScript. Open index.esm.js and index.umd.min.js and confirm the latter is minified to one line. If the UMD is not minified you forgot the terser() plugin in the UMD output block.

Step 6: Dry-run the publish

bash
npm publish --dry-run --access=public

This prints what npm would upload without actually uploading. Read the file list. If you see src/, tests/, or .env, your files array is wrong. Fix it before continuing. Cleaning this up before a real publish is how you avoid the embarrassing GitHub thread titled "I accidentally published my AWS credentials to npm."

Step 7: Publish

bash
npm publish --access=public

The --access=public flag is required for scoped packages on the free tier. Without it npm assumes the package is private and rejects the publish. Within seconds your package is live at https://npmjs.com/package/@jane-doe/sleep-promise. Bookmark that URL because it goes on your resume.

Step 8: Install it in another project to prove it works

Make a throwaway folder somewhere else on your machine:

bash
mkdir /tmp/proof && cd /tmp/proof
npm init -y
npm install @jane-doe/sleep-promise
node -e "import('@jane-doe/sleep-promise').then(m => m.sleep(500).then(() => console.log('works')))"

If "works" prints after half a second, the loop is closed. Take a screenshot for your portfolio site.

Tree-shaking, bundle analysis, and the CI release pipeline

Tree-shaking is the part of bundling interviewers love to ask about because it separates students who memorized the word from students who can demonstrate it. Here is a demo with real measurements.

Make a new test file src/tree-shake-demo.js:

javascript
import { debounce } from 'lodash-es';

window.handler = debounce(() => console.log('clicked'), 250);

Bundle it with lodash-es included (no external):

bash
npx rollup src/tree-shake-demo.js \
  --file dist/with-tree-shake.js \
  --format es \
  --plugin @rollup/plugin-node-resolve

Now bundle the same file but disable tree-shaking explicitly:

bash
npx rollup src/tree-shake-demo.js \
  --file dist/no-tree-shake.js \
  --format es \
  --no-treeshake \
  --plugin @rollup/plugin-node-resolve

Check the sizes:

bash
ls -la dist/with-tree-shake.js dist/no-tree-shake.js

On my run the tree-shaken bundle is 12 kilobytes; the non-tree-shaken bundle pulls in most of lodash and weighs about 95 kilobytes. That is roughly an 87 percent size reduction for a single-function import. Put that number on your resume bullet: "Reduced library bundle size 87% via ES module tree-shaking with Rollup 4." It is honest, measurable, and demonstrates principle.

The lesson hiding inside the lesson: tree-shaking only works on ES modules. If you imported from lodash instead of lodash-es (the CommonJS version), Rollup cannot statically trace the exports and you get the full 95-kilobyte payload. Choosing ESM dependencies is the actual performance lever; Rollup is the tool that exposes it.

Bundle analysis: looking at what you actually shipped

Install the visualizer plugin:

bash
npm install --save-dev rollup-plugin-visualizer

Add it to the bottom of the plugins array in rollup.config.js:

javascript
import { visualizer } from 'rollup-plugin-visualizer';

// ...inside the config object's plugins array:
plugins: [
  resolve(),
  commonjs(),
  typescript(),
  visualizer({ filename: 'dist/stats.html', open: false }),
],

Rebuild and open dist/stats.html in your browser. You get a treemap of every module in your bundle sized by byte count. When a hiring manager asks "how do you debug bundle bloat" you do not say "I read the bundle"; you say "I run rollup-plugin-visualizer, find the largest box, and either tree-shake it, lazy-load it, or replace it with a lighter dep." That answer wins phone screens.

A useful follow-up exercise is to add a deliberately heavy dependency, like the full lodash (not lodash-es), build, and look at the visualizer output. You will see one huge orange block dominating the treemap. Replace it with lodash-es plus named imports, rebuild, and watch the orange block shrink to a sliver. This little experiment is the single most useful thing you can demonstrate live in a phone screen because it shows the cause-and-effect chain end to end: package-format choice changes bundler behavior changes shipping cost. That is the kind of mental model engineers hire for.

GitHub Actions: automated npm publish on tag

The final resume signal is automation. Hand-publishing from your laptop is fine for the first release; automated CI is what looks professional. Create .github/workflows/publish.yml:

yaml
name: Publish to npm

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '24'
          registry-url: 'https://registry.npmjs.org'
      - run: npm ci
      - run: npm test
      - run: npm run build
      - run: npm publish --access=public --provenance
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Two things make this stand out from junior CI files. First, permissions: id-token: write and --provenance enable npm's provenance attestations: a cryptographic claim signed by GitHub that the package was built from a specific commit. Provenance shows up as a verified badge on npmjs.com and is impossible to fake. Second, the workflow is triggered on tags, not on every push, so you only ship on intentional releases. Generate an automation token in your npm account settings, paste it into your repo's Actions secrets as NPM_TOKEN, and the loop is closed. From now on git tag v0.2.0 && git push --tags publishes a new version with a verified provenance attestation. That is a single resume bullet that beats most undergrad portfolio projects.

A small but useful improvement: add a separate ci.yml workflow that runs on every push and PR, executes npm ci, npm run lint, npm test, and npm run build, but does not publish. Now you have two workflows: continuous integration on every commit, and a publish workflow only on tag pushes. That separation is exactly what production teams do and gives you another concrete resume bullet: "Set up separate CI and release pipelines in GitHub Actions, with npm provenance on tagged releases." Hiring managers know what that sentence means and it instantly puts you ahead of candidates whose only build experience is npm run build on their laptop.

If you want to go further, add a release-please step or a changesets-based workflow that automatically bumps the version and writes a CHANGELOG when commits land on main. That is genuinely advanced for an undergrad and is the kind of detail that turns a phone screen into an onsite invite. Do not feel obligated to do it on your first package; come back to it on the second one.

When Rollup beats Vite and esbuild for student work

Students reach for the wrong bundler all the time because the marketing pages all say "fast." Here is the actual decision matrix.

You are buildingPickWhy
A web app users visit in a browserViteVite gives you instant HMR for dev and uses Rollup under the hood for production builds. Best of both worlds.
A library other developers will install from npmRollupLibrary-first design, multi-format output, tree-shakeable consumer-friendly output.
A CLI tool or Node scriptesbuild or Rollupesbuild is fastest for single-output CJS. Rollup wins when you also need ESM.
A capstone full-stack web appVite (frontend) + tsx or Rollup (backend)Use the right tool per layer. Vite for the React/Vue side, Rollup if you also publish a shared library.
Coursework that just needs to runViteLowest friction, fewest decisions.

The non-obvious takeaway: learning Rollup teaches you what Vite is doing on production builds. Vite hides the bundler at dev time; Rollup is the bundler at deploy time. When the on-call engineer asks "why did our prod build break but dev works fine?" the answer is almost always a Rollup config issue. Knowing Rollup is knowing how Vite ships code.

It is also worth knowing where esbuild fits, because the answer is more interesting than "it is faster." esbuild's superpower is speed (it is written in Go and skips the Node startup cost) and that makes it perfect for two specific places: dev-time transformation, and quick bundling of single-output Node CLIs. esbuild's tree-shaking is good but not as thorough as Rollup's, its plugin API is smaller, and its multi-format output story is weaker. For a library you intend other developers to consume, Rollup remains the right pick. For a Lambda function or a single executable script, esbuild is faster to set up and just as effective. Vite combines both: esbuild during development for instant feedback, Rollup at build time for clean production output.

There is one more student-relevant angle. When you list "Rollup" on your resume, you are signaling that you understand bundlers as a category. You can talk about entry points, output formats, tree-shaking, source maps, and plugin pipelines. Listing "Vite" alone signals familiarity with one specific tool. Listing "Rollup" plus a real npm package signals you understand the substrate Vite is built on. Both are good signals; the second one is rarer and harder to fake, which is exactly why it punches above its weight in phone screens.

Common pitfalls and how to dodge them

Pitfall 1: forgetting external and shipping a 2 MB bundle. Mark every dependency in package.json as external in the Rollup config or your library will ship its dependencies inline. Library users will hate you, and reviewers will ding the package for bloat.

Pitfall 2: publishing your src folder. Without a files array in package.json, npm publishes everything not in .npmignore. That ships your TypeScript source, tests, and any dotfiles you forgot. Set "files": ["dist", "README.md", "LICENSE"] and dry-run before publish.

Pitfall 3: not setting "type": "module" consistently. If package.json has "type": "module", your .js files are interpreted as ESM. Rollup config in CJS syntax will then break. Use rollup.config.mjs (or rename to .mjs) to avoid the trap, or write the config in ESM throughout.

Pitfall 4: shipping ESM-only and discovering older toolchains break. If you publish only format: 'es', half the world cannot consume your package. Always ship CJS + ESM at minimum. The exports field with the conditional import/require keys is what lets one package satisfy both consumers.

Pitfall 5: forgetting --access=public on a scoped package. npm publish will refuse with a billing error because npm assumes scoped packages are private. The flag is mandatory.

Pitfall 6: leaving .env in the repo root. Set up .gitignore and confirm npm pack (a non-mutating version of publish) does not include it. Real students have leaked AWS keys this way.

Pitfall 7: forgetting source maps. Real production libraries ship source maps so consumers can debug into your code from their browser devtools. Set sourcemap: true on every output block. The maps are tiny relative to your bundle and the goodwill they generate is large.

Pitfall 8: using a default export when consumers expect named exports. A default export attaches all your library's surface to one object. Bundlers cannot tree-shake an unused method off a default-exported object the way they can drop an unused named export. Prefer named exports for library entry points and use default only for single-purpose libraries (a single hook, a single component, a single function).

Three hypothetical student npm packages worth shipping

If you need an idea for a first package, here are three that are small enough to finish in a weekend and useful enough to put on a resume.

1. @yourname/sleep-promise is a 20-line Promise-based sleep utility for async/await code. Single function, no dependencies, ESM + CJS + UMD outputs. Demonstrates ESM publishing, multi-format output, and that you understand what a Promise is. Total work: one evening including tests.

2. @yourname/react-debounced-input is a React component that wraps <input> with debouncing built in. One peer dependency on React, demonstrates the external config, JSX through @rollup/plugin-babel or the TypeScript plugin's JSX support, and a real component published to npm. Total work: one weekend.

3. @yourname/safe-fetch is a typed wrapper around fetch that returns a discriminated union { ok: true, data } | { ok: false, error } instead of throwing. Demonstrates TypeScript publishing with .d.ts types, generics, and modern error-handling idioms. Recruiters at every TypeScript-heavy shop will look at this. Total work: one weekend.

Each of these gives you a real npm URL, a real GitHub repo, a real CI badge, and a real conversation starter for a phone screen.

What to put on the resume

Once you have shipped one of the above, the resume bullet writes itself:

Published @jane-doe/sleep-promise to npm (1.2k weekly downloads). Bundled with Rollup 4 in ES/CJS/UMD outputs; achieved 87% bundle size reduction via tree-shaking. Automated releases via GitHub Actions with npm provenance attestations.

Three numbers, three tools, one URL. That single line answers more interview questions than a four-bullet description of a class project.

A small addendum to keep in mind: be honest with the numbers. If your package has 12 weekly downloads (you, the npm bot, your roommate), say "demoed end-to-end open-source release" rather than fabricating download counts. Recruiters routinely check npm stats and a single embellishment can blow the interview. The package itself is the proof artifact; the resume bullet just points at it.

For deeper TypeScript fundamentals that pair with Rollup library authoring, read the TypeScript best practices guide. For the npm side of the story including versioning and semver, see the npm best practices guide. For the CI step in detail including matrix builds and secrets management, work through the GitHub Actions best practices guide. The Launch School hub has the full set of resume-grade undergrad guides.

External authority: the official Rollup site and the npm publish CLI reference are the two pages worth bookmarking permanently.

FAQ

Is Rollup the same as Webpack?

No. Both bundle JavaScript but they target different use cases. Webpack is a general-purpose application bundler with a large runtime and a deep plugin ecosystem; it shines for complex web apps with code splitting, asset pipelines, and dev servers. Rollup is a library-first ES module bundler with a small surface area and clean output; it shines for code other developers will install from npm. Most undergrads should learn Rollup first because the output is human-readable and the configuration is short enough to fit on a single screen. Once you understand Rollup, picking up Webpack later is straightforward; the concepts (entry, output, loaders/plugins, code splitting) map directly.

Does Vite use Rollup?

Yes, for production builds. Vite uses esbuild for fast dependency pre-bundling and dev-time transformations, and Rollup for the final production build. That means if you understand Rollup, you understand 90% of what happens when you run vite build. Learning Rollup is the highest-leverage way to demystify Vite. Many of Vite's plugin hooks are literally Rollup hooks, which is why most Rollup plugins also work in Vite production builds with little or no modification.

Should I use Rollup or esbuild for my CLI tool?

For a simple Node.js CLI with one entry point and no need for ESM consumers, esbuild is fastest and easiest. For a CLI that you also want to expose as a library (so other Node programs can import its functions), Rollup is the better choice because it produces clean ESM + CJS output side-by-side. Many real-world tools (like Vite itself) use both: esbuild for speed-critical transforms, Rollup for the published bundle.

How do I make sure my npm package is tree-shakeable?

Three rules. First, set "sideEffects": false in package.json if your library has no side effects (no top-level mutation of globals, no CSS imports, no DOM modification at import time). This is the explicit signal bundlers need to drop unused exports. Second, publish an ES module output (format: 'es' in Rollup). Third, use named exports rather than a default export with attached methods, because named exports are easier for bundlers to trace. The combination is what made lodash-es shrinkable from 70 kilobytes to a few hundred bytes when only one function is used.

Can I publish a TypeScript library with Rollup?

Yes, and it is the resume-grade path. Use @rollup/plugin-typescript with a tsconfig.json that has declaration: true and declarationDir: "./dist". Rollup will emit your bundled JS plus a .d.ts file. Point "types": "dist/index.d.ts" in package.json and your library now ships full type information to every TypeScript consumer. This is the difference between an amateur package and a professional one.

What is the difference between main, module, and exports in package.json?

main is the legacy CommonJS entry point read by old Node.js and old bundlers. module is the ESM entry point read by modern bundlers like Rollup, Webpack 5, and Vite (it is what lets them tree-shake your code). exports is the modern conditional-export field read by Node.js 14+ and modern bundlers; it can return a different file based on whether the consumer is using import (ESM) or require (CJS) and is the recommended way to declare entry points today. For maximum compatibility, set all three and keep them consistent.

How do I get my npm package noticed?

Real talk: a single first-package release is for your resume, not for users. Aim for one or two non-trivial features, a clean README, real tests, and a GitHub repo with a green CI badge. Tweet the release once, post it to the r/javascript weekly thread, and link it from your portfolio site. Do not expect 10k downloads on a first release; expect 12 weekly downloads (you, your laptop, the npm bot, your roommate testing it). The point is that the package exists and is real, not that it is famous.

How do I update a published package safely?

Bump the version with npm version patch (or minor/major), which writes package.json and creates a git tag. Push the tag with git push --tags and your GitHub Actions release workflow publishes the new version. Never edit a published version in place because npm publishes are immutable for 72 hours and even after that, unpublishing is socially destructive. Treat every npm publish as a permanent release.


Built 2026-05-20. Solomon Signal Launch School use-cases page. Resume-grade by design.

Read the full Rollup for College Students review

Rollup for College Students Use Cases FAQ

Common questions about applying Rollup for College Students to real workflows

No. Both bundle JavaScript but target different use cases. Webpack is a general-purpose application bundler with a large runtime and deep plugin ecosystem; it shines for complex web apps with code splitting, asset pipelines, and dev servers. Rollup is a library-first ES module bundler with a small surface area and clean output; it shines for code other developers will install from npm. Most undergrads should learn Rollup first because the output is human-readable and the configuration is short enough to fit on a single screen. Once you understand Rollup, picking up Webpack later is straightforward; the concepts (entry, output, loaders/plugins, code splitting) map directly.
Yes, for production builds. Vite uses esbuild for fast dependency pre-bundling and dev-time transformations, and Rollup for the final production build. That means if you understand Rollup, you understand 90% of what happens when you run vite build. Learning Rollup is the highest-leverage way to demystify Vite. Many of Vite's plugin hooks are literally Rollup hooks, which is why most Rollup plugins also work in Vite production builds with little or no modification.
For a simple Node.js CLI with one entry point and no need for ESM consumers, esbuild is fastest and easiest. For a CLI that you also want to expose as a library (so other Node programs can import its functions), Rollup is the better choice because it produces clean ESM + CJS output side-by-side. Many real-world tools (like Vite itself) use both: esbuild for speed-critical transforms, Rollup for the published bundle.
Three rules. First, set sideEffects: false in package.json if your library has no side effects (no top-level mutation of globals, no CSS imports, no DOM modification at import time). This is the explicit signal bundlers need to drop unused exports. Second, publish an ES module output (format: 'es' in Rollup). Third, use named exports rather than a default export with attached methods, because named exports are easier for bundlers to trace. The combination is what made lodash-es shrinkable from 70 kilobytes to a few hundred bytes when only one function is used.
Yes, and it is the resume-grade path. Use @rollup/plugin-typescript with a tsconfig.json that has declaration: true and declarationDir: './dist'. Rollup will emit your bundled JS plus a .d.ts file. Point types: 'dist/index.d.ts' in package.json and your library now ships full type information to every TypeScript consumer. This is the difference between an amateur package and a professional one.
main is the legacy CommonJS entry point read by old Node.js and old bundlers. module is the ESM entry point read by modern bundlers like Rollup, Webpack 5, and Vite (it is what lets them tree-shake your code). exports is the modern conditional-export field read by Node.js 14+ and modern bundlers; it can return a different file based on whether the consumer is using import (ESM) or require (CJS) and is the recommended way to declare entry points today. For maximum compatibility, set all three and keep them consistent.
Real talk: a single first-package release is for your resume, not for users. Aim for one or two non-trivial features, a clean README, real tests, and a GitHub repo with a green CI badge. Tweet the release once, post it to the r/javascript weekly thread, and link it from your portfolio site. Do not expect 10k downloads on a first release; expect 12 weekly downloads (you, your laptop, the npm bot, your roommate testing it). The point is that the package exists and is real, not that it is famous.
Bump the version with npm version patch (or minor/major), which writes package.json and creates a git tag. Push the tag with git push --tags and your GitHub Actions release workflow publishes the new version. Never edit a published version in place because npm publishes are immutable for 72 hours and even after that, unpublishing is socially destructive. Treat every npm publish as a permanent release.