How to Init a TypeScript Project for a React Application
To init a TypeScript project for a React application, configure tsconfig.json with "jsx": "react-jsx" to enable the automatic JSX runtime, enable "strict": true for comprehensive type checking, and set "module": "ESNext" with "moduleResolution": "NodeNext" for optimal bundler compatibility.
Initializing a TypeScript project for a React application requires specific compiler options to leverage modern JSX transforms and ensure long-term maintainability. According to the microsoft/TypeScript source code, the compiler defines these behaviors in src/compiler/commandLineParser.ts and references base defaults in src/tsconfig-base.json. This guide distills the essential configuration steps derived from the TypeScript repository itself to optimize performance and type safety in React codebases.
Essential Compiler Flags for React
When configuring a React project, specific flags in tsconfig.json control JSX emission, module resolution, and build performance. The following options are defined in the TypeScript compiler source and represent the current best practices:
-
"jsx": "react-jsx"– Enables the automatic JSX runtime introduced in React 17. This eliminates the need forimport React from "react"in every file and produces smaller bundle sizes. The valid values for this flag are defined insrc/compiler/commandLineParser.tsat lines 40-55. -
"jsxImportSource": "react"– Specifies the module specifier for the JSX factory functions. If using alternative frameworks like Preact or Solid, you can redirect this to their respective packages. The diagnostic messages for this option are located insrc/compiler/diagnosticMessages.jsonat lines 6288-6290. -
"module": "ESNext"and"moduleResolution": "NodeNext"– Aligns emitted code with modern bundlers such as Vite, Webpack 5, or ESBuild. The TypeScript repository itself usesNodeNextas the default resolution strategy insrc/tsconfig-base.jsonat lines 9-11. -
"strict": true– Activates the full suite of strictness checks includingnoImplicitAny,strictNullChecks, andstrictFunctionTypes. This catches potential runtime errors during compilation. -
"isolatedModules": true– Ensures each file can be transpiled independently, which is required by fast transpilers like ESBuild and Babel. This prevents cross-file dependencies during the emit phase. -
"skipLibCheck": true– Bypasses type checking for declaration files innode_modules, significantly reducing compilation time without affecting your application's type safety. -
"esModuleInterop": trueand"allowSyntheticDefaultImports": true– Facilitates ergonomic imports from CommonJS modules such asreact-dom.
Recommended Configuration Structure
Create a tsconfig.json that extends a base configuration, following the architectural pattern used throughout the microsoft/TypeScript repository. The base config at src/tsconfig-base.json enables "composite": true and "incremental": true at lines 13-17, which are essential for project references and fast rebuilds.
{
"extends": "./tsconfig-base.json",
"compilerOptions": {
"target": "ES2020",
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"resolveJsonModule": true,
"types": ["react", "react-dom"]
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
The "include": ["src"] directive restricts the compiler to your source directory, reducing the scope of type checking and improving performance. Extending a base configuration keeps shared settings DRY while allowing project-specific overrides.
Installing Dependencies
Install the compiler and React type definitions as development dependencies, while keeping React itself as a runtime dependency:
npm install -D typescript @types/react @types/react-dom
npm install react react-dom
The @types packages provide declaration files that enable IntelliSense and type checking for the React APIs without including source code in your production bundle.
Enabling Fast Incremental Builds
The TypeScript compiler supports incremental compilation through the "incremental" and "composite" flags. When these are enabled in your base configuration (as seen in src/tsconfig-base.json), the compiler generates a tsconfig.tsbuildinfo cache file that tracks the state of the compilation.
To leverage this in your React project, use the build mode commands:
tsc -b # Build using the cache
tsc -b --watch # Watch mode for development
The build mode (-b) respects project references and only recompiles files that have changed or whose dependencies have changed, dramatically reducing build times in large codebases.
JSX Pragma and Codefixes
The TypeScript compiler recognizes JSX pragmas such as /** @jsx */ and /** @jsxImportSource */ defined in src/compiler/types.ts at lines 10266-10278. These comments allow per-file overrides of the JSX factory.
If you omit the "jsx" flag, the language service can automatically suggest a fix. The implementation in src/services/codefixes/fixEnableJsxFlag.ts at line 42 demonstrates how the compiler mutates tsconfig.json to add "jsx": "react" when JSX is detected without the proper configuration.
Maintainability Best Practices
Separate concerns by maintaining a root-level tsconfig-base.json with universal settings, and project-specific tsconfig.json files that extend it. This mirrors the structure of the microsoft/TypeScript repository itself.
Avoid the any type by enforcing strict null checks. When you need temporary escape hatches, prefer unknown with type guards over any.
Configure path mapping to simplify imports in deep directory structures:
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"components/*": ["components/*"],
"utils/*": ["utils/*"]
}
}
}
Practical Code Examples
Basic React Entry Point
With "jsx": "react-jsx" configured, you no longer need to import React explicitly:
import { createRoot } from "react-dom/client";
import App from "./App";
const root = createRoot(document.getElementById("root")!);
root.render(<App />);
The compiler automatically injects the JSX runtime import based on the pragma definitions in src/compiler/types.ts.
Custom JSX Factory Configuration
To use Preact instead of React, modify the import source:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
The corresponding source file imports from Preact:
import { render } from "preact";
import App from "./App";
render(<App />, document.body);
Build Scripts Configuration
Add the following scripts to your package.json to leverage incremental compilation:
{
"scripts": {
"build": "tsc -b",
"watch": "tsc -b --watch",
"type-check": "tsc --noEmit"
}
}
The type-check script runs a full type check without emitting files, useful for CI pipelines where the bundler handles transpilation.
Summary
- Configure
tsconfig.jsonwith"jsx": "react-jsx"to utilize React 17's automatic runtime, eliminating redundant imports and reducing bundle size. - Enable strict mode (
"strict": true) to catch null pointer exceptions and implicit any types before runtime. - Use modern module resolution (
"moduleResolution": "NodeNext") as implemented insrc/tsconfig-base.jsonto ensure compatibility with ES modules. - Leverage incremental builds via
tsc -band the"composite": truesetting to cache compilation state and speed up rebuilds. - Reference source locations such as
src/compiler/commandLineParser.tsandsrc/services/codefixes/fixEnableJsxFlag.tsto understand how the compiler validates and fixes configuration.
Frequently Asked Questions
What is the difference between "jsx": "react" and "jsx": "react-jsx"?
The "react" setting transforms JSX into React.createElement calls and requires an explicit import React from "react" in every file. The "react-jsx" setting, defined in src/compiler/commandLineParser.ts, uses the automatic JSX runtime introduced in React 17, injecting the jsx runtime functions automatically and allowing you to omit the React import.
Why is "isolatedModules": true required for many React projects?
This flag ensures each file can be transpiled independently without cross-file type information, which is necessary for transpilers like Babel and ESBuild that process files in parallel. As noted in the TypeScript diagnostic system, this prevents subtle runtime errors when const enums or types are used across file boundaries in non-TypeScript transpilation pipelines.
How do I enable incremental compilation in a TypeScript React project?
Set "incremental": true and "composite": true in your base configuration (as seen in src/tsconfig-base.json lines 13-17), then use tsc -b instead of tsc to build. The compiler generates a .tsbuildinfo file that caches the dependency graph and emitted file hashes, allowing subsequent builds to skip unchanged files.
Where are JSX pragma comments like /** @jsx */ handled in the TypeScript source?
Pragma definitions for JSX directives are stored in src/compiler/types.ts at lines 10266-10278. These pragmas allow per-file overrides of the JSX factory function and import source, providing flexibility when mixing different JSX frameworks within a single project or migrating between them.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →