Skip to content
TypeScript ts react 3 min read

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 the vite-plugin-checker) in CI and in your build script 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
PackagePurpose
react / react-domThe runtime libraries
@types/reactTypes for components, hooks, ReactNode, events
@types/react-domTypes for createRoot, ReactDOM, portals
typescriptThe 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-ts instead of hand-rolling config.
  • Always enable "strict": true; it is what makes typed React catch real bugs.
  • Run tsc --noEmit in CI since Vite/esbuild does not type-check during dev.
  • Use "jsx": "react-jsx" so you never need to import React just for JSX.
  • Keep @types/react aligned with your installed react major version.
  • Use .tsx for files with JSX and .ts for pure logic and type modules.
  • Narrow document.getElementById(...) before passing it to createRoot.
Last updated June 29, 2026
Was this helpful?