TypeScript with React Setup
Adding TypeScript to a React project gives you autocomplete on props, compile-time checks on hooks, and self-documenting components — and modern tooling makes the setup almost effortless. The fastest path is Vite’s react-ts template, which wires up the compiler, JSX support, and the React type packages for you. This page walks through scaffolding a project, the type packages you need, the compiler options that matter for React, and how files and components should be named. Everything here assumes TypeScript 5.x and React 18+.
Scaffolding with Vite
Vite ships a ready-made React + TypeScript template, so you rarely need to configure the compiler by hand. Run the scaffolder and pick the TypeScript variant.
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
This generates .tsx files, a working tsconfig.json, and the @vitejs/plugin-react plugin that handles JSX transformation and Fast Refresh. Vite uses esbuild to strip types during development for near-instant builds, and runs tsc only when you ask for a type-checked production build.
Vite does not type-check on every save — esbuild only erases types. Run
tsc --noEmit(or thevite-plugin-checker) in CI and in yourbuildscript so type errors actually fail the build.
Installing the type packages
React’s types live in separate @types packages because React itself is written without bundled declarations. The Vite template includes them, but for a manual setup install them as dev dependencies.
npm install react react-dom
npm install -D typescript @types/react @types/react-dom
| Package | Purpose |
|---|---|
react / react-dom | The runtime libraries |
@types/react | Types for components, hooks, ReactNode, events |
@types/react-dom | Types for createRoot, ReactDOM, portals |
typescript | The tsc compiler and language service |
Keep @types/react and @types/react-dom on the same major version as your installed react to avoid mismatched declarations.
The tsconfig for React
A React project needs jsx set so the compiler understands JSX, plus the DOM libs and strict mode. Vite’s template produces something close to this.
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx", // new JSX transform — no need to import React
"strict": true,
"noEmit": true, // Vite/esbuild emits, tsc only checks
"skipLibCheck": true,
"isolatedModules": true
},
"include": ["src"]
}
The "jsx": "react-jsx" setting enables the automatic runtime, so you no longer write import React from "react" in every file. strict: true is the single most valuable flag — it turns on strictNullChecks and noImplicitAny, which is exactly what makes typed React worthwhile.
Naming files and the entry point
Components that contain JSX must use the .tsx extension; plain logic and type files use .ts. The application entry point creates the root and renders your top-level component.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
const rootEl = document.getElementById("root");
if (!rootEl) throw new Error("Root element not found");
createRoot(rootEl).render(
<StrictMode>
<App />
</StrictMode>,
);
Because getElementById returns HTMLElement | null under strictNullChecks, you must narrow away the null before passing it to createRoot. The explicit guard is the idiomatic fix and gives a clear error if the DOM node is missing.
A first typed component
With the project scaffolded, a component is just a function that returns JSX. Annotate its props with an interface and let TypeScript infer the return type.
interface GreetingProps {
name: string;
}
function Greeting({ name }: GreetingProps) {
return <h1>Hello, {name}!</h1>;
}
// ❌ Error: Property 'name' is missing in type '{}'
// <Greeting />
The compiler now enforces the contract at every call site: forgetting name, or passing a number, is a build error rather than a runtime surprise. From here the rest of this chapter builds on this foundation — props, state, events, and hooks.
Best Practices
- Scaffold new projects with
npm create vite@latest -- --template react-tsinstead of hand-rolling config. - Always enable
"strict": true; it is what makes typed React catch real bugs. - Run
tsc --noEmitin CI since Vite/esbuild does not type-check during dev. - Use
"jsx": "react-jsx"so you never need to importReactjust for JSX. - Keep
@types/reactaligned with your installedreactmajor version. - Use
.tsxfor files with JSX and.tsfor pure logic and type modules. - Narrow
document.getElementById(...)before passing it tocreateRoot.