vercel/ncc: compiling a Node.js project into one file
Compile a Node.js project into a single file. Supports TypeScript, binary addons, dynamic requires.
At a glance
- What is it?
- ncc bundles a Node.js entry point and its dependencies into a single output file with zero configuration, and it handles TypeScript, binary addons and dynamic requires. It is a build-time tool for publishing small npm packages and shipping lean serverless functions, not a general-purpose bundler.
- Who is it for?
- Adopt ncc when the output is a Node.js program that will run on a Node.js runtime and you want one file without writing a bundler config: publishing a small npm package, or shipping a serverless handler with its dependencies inlined. Do not adopt it when your target is a browser bundle, when your build already lives inside a webpack or esbuild pipeline you control, or when your code loads assets through paths the static evaluator cannot follow.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 48 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem ncc solves: one Node.js file, no bundler config
Node.js projects ship as a directory: an entry point, a node_modules tree, and whatever assets the code reads at runtime. That layout is fine locally and awkward everywhere else. The README lists the motivations directly: publish minimal packages to npm, ship only relevant app code to serverless environments, avoid configuring bundlers, and get faster bootup with less I/O overhead. The stated design goal is a compiled-language-like experience, with go named as the comparison.
The audience follows from that list. If you maintain a CLI, a small library, or a serverless function and you have ever written a webpack config just to inline your own dependencies, ncc is aimed at you. The design goals are narrow on purpose: zero configuration, TypeScript built in, and only Node.js programs as input and output. That last constraint is the one to remember. This is not a browser bundler and does not pretend to be one.
How ncc turns an entry point into a single file
The repository layout shows the shape of the tool. src/ holds the implementation, dist/ holds the compiled package that npm actually publishes, and patches/ exists because ncc is built on top of webpack and the asset relocator loader. The package.json devDependencies include @vercel/webpack-asset-relocator-loader at a pinned version, which is the component that decides where files referenced by your code end up in the output.
The data flow is: you point ncc at an entry file, it walks the require graph statically, resolves each dependency to a module, and emits one JavaScript file plus any assets that the static evaluator decided must travel with it. The README points at the relocator loader's own explanation of how that evaluation works. Because it is static analysis, the caveat section is explicit: files and assets are relocated based on a static evaluator, and dynamic non-statically analyzable asset loads may not work out correctly. That single sentence is the boundary of the tool.
Module format is handled automatically. If you build an .mjs file, or a .js file inside a package with "type": "module", ncc emits an ES module. If the input uses a .cjs extension, the output does too, which the README notes is useful for packages that want to keep .js files as ES modules under a "type": "module" boundary. Output naming follows the input: dist/index.js for input.js.
Installing ncc and building a first bundle
The README gives a single global install command. After it completes, the ncc executable is on your PATH.
npm i -g @vercel/nccThe canonical build invocation takes an input file and an output directory, which defaults to dist. Running it against a simple entry point should leave you with dist/index.js and a short build summary printed to the terminal.
ncc build input.js -o distFor a TypeScript entry point the only change is the file extension, but a tsconfig.json is required. The README's example sets a target of es2015 and node module resolution, which is the combination that matches ncc's own default output target.
{
"compilerOptions": {
"target": "es2015",
"moduleResolution": "node"
}
}If TypeScript appears in devDependencies, that version is the one ncc uses. To check the bundle actually runs before you publish it, the run subcommand builds into a temporary directory and executes the result with full source map support.
ncc run input.jsWhen you need a dependency left as a runtime require rather than inlined, pass it as an external. The flag can be repeated.
ncc build input.js -o dist -e externalpackageWhere ncc breaks: dynamic requires, assets and binary addons
The documented caveat is the important one. Asset relocation depends on a static evaluator, so any file path your code constructs at runtime, rather than writing literally, is a candidate for failure. A template string that builds a path to a config file, a plugin directory scanned with readdir, a locale file selected from a variable: none of these are guaranteed to survive the build. The README does not claim otherwise. If your program's behaviour depends on files it discovers rather than files it names, test the built output, not the source.
Binary addons are listed as supported in the project description, and the repository contains a test/binary directory plus a build-test-binary script that compiles a native module with node-gyp and copies the result into the integration fixtures. That tells you native modules are exercised in the test suite. It does not tell you that your particular prebuilt .node file will relocate correctly, and the README does not document a fallback when it does not.
The wrong-tool case is straightforward. If you are producing a browser bundle, ncc is the wrong tool by its own design goals, which restrict input and output to Node.js programs. If you already own a webpack or esbuild configuration that handles your assets, adding ncc on top means two systems deciding where the same files go. And if your application loads most of its behaviour from files at runtime, the static evaluator is working against you rather than for you.
ncc compared with esbuild and hand-written webpack configs
The obvious alternative is esbuild, which also produces a single output file from a Node.js entry point and does it with a different mechanism: a Go-based bundler that parses and transforms source directly rather than driving webpack. The practical difference is in what each one decides for you. esbuild exposes a configuration surface: define flags, external rules, platform and format settings, plugins. ncc's stated design goal is zero configuration, and its options list is short and mostly about output shape (minify, source map, target, externals, asset builds) rather than about resolution behaviour.
That difference cuts both ways. With ncc you get the asset relocator and its static evaluator, which is the piece that tries to move files your code references into the output. With esbuild you get speed and a plugin API, but you are responsible for telling it what to do with non-JavaScript files. Neither is strictly better; they encode different assumptions about how much of your project is statically knowable.
The second alternative is the webpack config you would otherwise write by hand. ncc is webpack underneath, with the asset relocator loader pinned in devDependencies and patches applied. Choosing ncc over a hand-written config is choosing to accept its defaults instead of tuning them. If your build already needs tuning, the abstraction is a cost, not a saving.
Programmatic use, watch mode and the cache
The Node API takes a path and an options object and resolves to code, map and assets. The assets value is an object keyed by asset file name, each entry carrying source, permissions and symlinks, expected relative to the output code. That is the detail that matters if you are writing a build script: you are responsible for writing those assets to disk yourself.
require('@vercel/ncc')('/path/to/input', {
cache: "./custom/cache/path" | false,
externals: ["externalpackage"],
minify: false,
sourceMap: false,
assetBuilds: false,
sourceMapRegister: true,
watch: false,
license: '',
target: 'es2015',
v8cache: false,
quiet: false,
debugLog: false
}).then(({ code, map, assets }) => {
console.log(code);
});Watch mode changes the contract. With watch: true the call does not return a promise; it returns an object with a handler invoked on each build completion, a rebuild function invoked when a rebuild starts, and a close function. Watch errors arrive on the err property of the handler argument. A build script written against the promise form will not work unchanged against the watcher.
Caching is on by default and can be pointed at a custom directory or turned off with cache: false, or skipped for a single CLI run with -C. The cache subcommand inspects it: cache clean, cache dir and cache size. The v8-cache option emits a build using the V8 compile cache, and the README does not state the size or startup difference it produces, so treat it as something to measure against your own entry point rather than assume.
Maintenance, licence and what a build costs you
The repository is not archived, and the last push was on 2026-08-13, the same day as the 0.45.0 release. The two prior releases, 0.44.0 and 0.44.1, landed in June 2026. The version string in package.json is 0.0.0-development, which means the published version is stamped at release time rather than committed. The project is still on a 0.x line, so minor version bumps are where behaviour changes will appear.
Upgrade cost is mostly about the pinned webpack asset relocator. The package.json pins @vercel/webpack-asset-relocator-loader to 1.10.3, and patches/ exists in the tree, so a change in that loader's relocation behaviour arrives with an ncc release rather than through a dependency range you control. If your build depends on a specific asset landing at a specific path, that is the thing to re-check after an upgrade.
The licence is MIT, and the package.json declares it. ncc can emit a file containing licensing information for the bundled dependencies via --license [file]. That matters because inlining dependencies into one file can obscure which licences apply to the shipped artifact. ncc generates the file; deciding whether the result satisfies your obligations is your call, and the README does not evaluate that question.
Editorial conclusion
Adopt ncc when the output is a Node.js program that will run on a Node.js runtime and you want one file without writing a bundler config: publishing a small npm package, or shipping a serverless handler with its dependencies inlined. Do not adopt it when your target is a browser bundle, when your build already lives inside a webpack or esbuild pipeline you control, or when your code loads assets through paths the static evaluator cannot follow. Before committing, verify three things on your own entry point: that every dynamically required module resolves, that asset-loading paths survive relocation, and that the licence file emitted by --license matches the dependencies you actually ship. Check the package-support.md notes for any dependency you rely on.
Frequently asked questions
What is vercel/ncc?
It is a CLI that compiles a Node.js module into a single file together with all its dependencies, in the style of a gcc-style compiler. Its design goals are zero configuration, TypeScript built in, and Node.js programs as the only input and output.
What is Vercel used for?
This article covers vercel/ncc, a CLI for compiling a Node.js module and its dependencies into a single file for npm packages and serverless environments. It does not describe the Vercel platform itself.
Is Vercel free to use?
Pricing for the Vercel platform is not covered here. The only licence fact available is that vercel/ncc is released under the MIT licence, as declared in package.json.
Is Vercel a CDN?
Vercel is not described as a CDN anywhere in this article. The subject here is vercel/ncc, which compiles a Node.js module into a single file together with its dependencies.
Can we deploy a node.js app on Vercel?
Deploying apps on the Vercel platform is outside this article's scope. What it does cover is ncc's aim of shipping only relevant app code to serverless environments, and its restriction of input and output to Node.js programs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vercel-ncc)
Community notes