moduleResolution bundler vs node16

moduleResolution bundler vs node16

Choose TypeScript moduleResolution settings for bundler-based apps, Node ESM, libraries, monorepos, package exports, and extensionless imports.

Open tsconfig Builder

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

ModeBest fitImportant behavior
bundlerNext.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.
node16Node.js projects that run emitted ESM/CommonJS directly in modern Node.Matches modern Node resolution behavior depending on whether output uses `import` or `require`.
nodenextNode.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 Builder

tsconfig resources

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