moduleResolution bundler vs node16
Choose TypeScript moduleResolution settings for bundler-based apps, Node ESM, libraries, monorepos, package exports, and extensionless imports.
Quick answer: use bundler or node16?
Use `moduleResolution: "bundler"` for apps compiled by Next.js, Vite, or another bundler. Use `node16` or `nodenext` when TypeScript output is meant to run directly in Node.js and must follow Node's ESM/CommonJS resolution rules.
bundler config example
`bundler` is usually the right default for frontend and full-stack framework apps because the bundler owns final module loading.
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "preserve",
"noEmit": true,
"strict": true
}
}node16 config example
`node16` is useful when emitted JavaScript should run in Node.js and TypeScript must model Node's separate import and require resolution paths.
{
"compilerOptions": {
"module": "Node16",
"moduleResolution": "node16",
"target": "ES2022",
"outDir": "dist",
"strict": true
}
}Package imports and exports
Both `bundler` and modern Node resolution modes understand package `imports` and `exports`. The practical difference is that bundler mode assumes a bundler will handle final runtime details.
- Use `bundler` when source imports are compiled by a framework or bundler.
- Use `node16` or `nodenext` when Node runs the emitted files.
- Test package boundary behavior when consuming ESM/CJS libraries from a monorepo.
Relative import extensions
The key developer-facing difference is extension handling. TypeScript's `bundler` mode does not require file extensions on relative imports, while Node ESM-style resolution can require runtime-correct file extensions.
// Common in bundler projects:
import { Button } from "./button";
// Often needed for emitted Node ESM:
import { createServer } from "./server.js";Next.js and Vite defaults
Framework apps usually pair `noEmit` with bundler-style resolution because the framework compiles, bundles, and serves the application. In those apps, TypeScript mostly checks types instead of producing runtime files.
- Next.js App Router projects typically keep framework-generated includes intact.
- Vite apps usually keep DOM libs and bundler resolution together.
- Library packages need a separate decision because emitted files may be consumed directly.
Monorepo and library caveats
Monorepos often need separate tsconfigs for apps and packages. An app can use bundler mode while a package that emits Node-ready files uses node16 or nodenext.
- Use separate app and package configs when runtime targets differ.
- Do not force one moduleResolution mode across every workspace by habit.
- Validate emitted package imports with the same runtime that will consume them.
Production checklist
Before changing moduleResolution, run typecheck, build, tests, and at least one runtime smoke test for generated output.
- Choose based on runtime: bundler-owned app or Node-owned output.
- Check relative imports after switching to node16 or nodenext.
- Check package exports/imports compatibility for dependencies.
- Use the tsconfig Builder to compare presets before editing a production config.
moduleResolution decision table
| Mode | Best fit | Important behavior |
|---|---|---|
| bundler | Next.js, Vite, modern frontend apps, and code compiled by a bundler. | Supports package `imports` and `exports` and does not require file extensions on relative imports. |
| node16 | Node.js projects that run emitted ESM/CommonJS directly in modern Node. | Matches modern Node resolution behavior depending on whether output uses `import` or `require`. |
| nodenext | Node.js projects that track current Node ESM behavior and package boundaries. | Similar intent to node16, but aligned with evolving NodeNext module behavior. |
Use FrameworkKit to generate the starter code, then review the output before shipping it in production.
Generate with tsconfig Buildertsconfig resources
tsconfig Builder
Generate TypeScript config presets for Next.js, Vite, Node, libraries, and strict mode projects.
tsconfig examples
Compare framework-aware TypeScript config starters before adapting your own project config.
tsconfig for Next.js
Start from a Next.js App Router TypeScript config and preserve generated route/framework types.
tsconfig Builder vs manual config
Decide when a generated config starter is enough and when a project needs manual compiler options.
FAQ
What is the difference between moduleResolution bundler and node16?
`bundler` is for code resolved by bundlers and does not require file extensions on relative imports. `node16` models modern Node.js ESM/CommonJS resolution for emitted JavaScript.
Should Next.js use moduleResolution bundler?
Bundler-style resolution is usually appropriate for Next.js apps because Next owns compilation and bundling. Preserve the framework's generated tsconfig includes when editing.
Should Node libraries use node16 or bundler?
Use `node16` or `nodenext` when the emitted package is meant to run directly in Node.js. Use `bundler` when the package is only consumed through a bundler.
Why did imports break after switching to node16?
Node ESM-style resolution may require runtime-correct file extensions and stricter package boundary behavior. Review relative imports and package exports after switching.
Related tools
JSON to Zod Converter
Convert JSON to Zod schemas with strict objects, optional field inference, and inferred TypeScript types.
Zod to JSON Schema Converter
Use a free browser-only online converter to turn Zod 4 schemas into JSON Schema for Draft 2020-12, Draft 7, AJV, or OpenAPI-compatible output.
Next Metadata Generator
Generate typed Next.js metadata, Open Graph fields, Twitter cards, and JSON-LD starters.