tsconfig.json Generator: Presets for Node, React, and Published Libraries
TypeScript's compiler options interact in ways that aren't always obvious — the right module/moduleResolution pairing depends on whether Node or a bundler is actually running your code, and options like noEmit only make sense for certain project shapes. This generator builds a sensible starting tsconfig.json from a project-type preset instead of assembling options from scratch.
Node.js: NodeNext/NodeNext. React/Next.js: ESNext/Bundler + DOM libs. Library: ESNext/Bundler + declarations.
Why Module Resolution Has to Match Your Runtime
moduleResolution controls how TypeScript figures out what file an import actually points to. NodeNext mirrors Node.js's own resolution algorithm (including package.json 'exports' and .js extension requirements in ESM), while Bundler mirrors how tools like webpack, Vite, and Next.js's compiler resolve imports — which is more permissive. Using the wrong one means TypeScript's type-checking doesn't match what will actually run.
noEmit vs. Declaration Output
Frontend projects using a bundler only need TypeScript for type-checking — the bundler itself handles turning TS into runnable JS, so noEmit avoids TypeScript also writing (and potentially conflicting with) build output. A published library has the opposite need: it must emit .d.ts declaration files so consumers of the package get type information, since there's no bundler involved for the library's own build step.
What strict Mode Actually Enables
strict is a bundle of several individual flags: strictNullChecks (no implicit undefined/null access), noImplicitAny, strictFunctionTypes, and others. Turning it on as a single project-wide option is far easier than adopting each flag individually later, since retrofitting strict null checks onto a large codebase after the fact is significantly more work.
Practical Examples
Setting Up a Next.js Project
Bundler-based frontend.
- 1.Preset: React / Next.js
- 2.module: ESNext, moduleResolution: Bundler
- 3.jsx: react-jsx, noEmit: true
Publishing an npm Library
Needs type declarations for consumers.
- 1.Preset: Published Library
- 2.declaration: true
- 3.outDir: dist (compiled output ships to npm)
What Each Preset Sets
- Node.js: NodeNext module/resolution, ES2022 target
- React/Next.js: Bundler resolution, DOM libs, react-jsx, noEmit
- Library: Bundler resolution, declaration file output
Toggles Available on Any Preset
- Strict mode: recommended for all new projects
- esModuleInterop: smoother default-import interop with CommonJS
- skipLibCheck: skip type-checking node_modules .d.ts files
- Declaration output and outDir
Frequently Asked Questions
What's the difference between the three presets?
Node.js targets a server runtime with NodeNext module resolution (matching how Node actually resolves imports). React/Next.js targets a bundler-based frontend with DOM libs, JSX support, and noEmit (since the bundler, not tsc, produces output). Library adds declaration file emission so consumers of your published package get types.
Why does React/Next.js default to noEmit?
In a bundler-based frontend project (Next.js, Vite, webpack), the bundler itself compiles and outputs your code — tsc is used only for type-checking. Setting noEmit tells TypeScript not to also try to write output files, avoiding conflicting or redundant build artifacts.
What does moduleResolution: Bundler actually change?
It tells TypeScript to resolve imports the way modern bundlers do (allowing extensionless imports, package.json 'exports' fields, etc.) rather than strictly following how Node.js resolves modules at runtime — appropriate when a bundler, not Node, is what actually runs your resolved imports.
Should I always turn on strict mode?
For new projects, yes — it catches an entire category of bugs (null/undefined access, implicit any) that non-strict mode allows silently. It's much harder to add strict mode to a large existing codebase after the fact than to start with it enabled.
What does skipLibCheck actually skip?
It skips type-checking of .d.ts declaration files in node_modules, which can otherwise fail due to conflicting type definitions between packages. It's almost always recommended — you're not responsible for fixing type errors in third-party dependencies.
Why generate declarations only for the Library preset?
Declaration files (.d.ts) let consumers of your published npm package get autocomplete and type-checking against your code. An application (not published as a library) has no consumers importing its types, so declaration output is unnecessary there.