TypeScript Setup for React Projects: Step-by-Step
Setting up TypeScript in a React project is now faster than ever—Vite, the modern build tool, ships with TypeScript support out of the box. In under 10 minutes, you can have a fully working Vite+React+TypeScript project with JSX, module resolution, and strict type checking enabled. This article walks you through the exact steps and explains each configuration choice so you understand why each setting matters for your React workflow.
How Do You Create a TypeScript React Project with Vite?
The fastest way is to use Vite's built-in template scaffolding. Vite automatically configures TypeScript, JSX transpilation, and module resolution when you scaffold with the react-ts template, eliminating manual config work.
Run this command in your terminal:
npm create vite@latest my-react-app -- --template react-ts
Then navigate into the project and install dependencies:
cd my-react-app
npm install
You now have a working TypeScript+React project. The scaffolder creates a tsconfig.json with sensible defaults, a vite.config.ts with JSX support pre-enabled, and starter component files with .tsx extensions. Run npm run dev to see your app live on localhost:5173.
What Is a tsconfig.json and Why Do You Need It?
A tsconfig.json is TypeScript's configuration file—it tells the TypeScript compiler how to interpret your code, which JavaScript target to emit, and which strict type-checking rules to enforce. Every TypeScript project needs one. The compiler uses it to validate your types before bundling, catching errors early in your development cycle rather than at runtime.
Here is the essential tsconfig.json for a React+Vite project:
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"resolveJsonModule": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
"jsx": "react-jsx",
"isolatedModules": true,
"types": ["vite/client"]
},
"include": ["src"],
"references": [{ "path": "./tsconfig.app.json" }]
}
The key options are:
target: "ES2020"— Emit JavaScript that runs on modern browsers (2020 feature level).jsx: "react-jsx"— Use React 17+ JSX transform (no need to import React in every file).strict: true— Enable all strict type-checking rules. This is non-negotiable for type safety.moduleResolution: "bundler"— Use modern module resolution that Vite understands.noEmit: true— Don't write.jsfiles; Vite handles bundling.
How Do You Configure JSX in TypeScript?
JSX is the syntax for writing component markup in React (return <div>Hello</div>). TypeScript needs to know how to handle it. The jsx compiler option in tsconfig.json tells TypeScript whether to emit JSX as function calls or to use the newer automatic JSX transform.
For modern React (17+), set "jsx": "react-jsx" in your tsconfig.json. This tells TypeScript to transform JSX like <Button /> into _jsx(Button, {}) without requiring an explicit React import:
// No React import needed!
export function App() {
return <div className="app">Welcome to React with TypeScript</div>;
}
If your project is older and uses React 16, use "jsx": "react" instead, which requires import React from 'react' at the top of files with JSX.
How Does Module Resolution Work in TypeScript for React?
Module resolution is how TypeScript finds the files you import. When you write import Button from './Button', TypeScript needs to locate the file and read its type definitions. The moduleResolution option determines the algorithm.
For Vite projects, always use "moduleResolution": "bundler". This uses the same resolution strategy Vite uses, so your TypeScript compiler agrees with your bundler about where to find files. Here is a practical example:
// src/components/Button.tsx
export interface ButtonProps {
label: string;
onClick: () => void;
}
export function Button({ label, onClick }: ButtonProps) {
return <button onClick={onClick}>{label}</button>;
}
// src/App.tsx
import { Button } from './components/Button';
export function App() {
const handleClick = () => console.log('Clicked!');
return <Button label="Click me" onClick={handleClick} />;
}
TypeScript resolves './components/Button' by looking for Button.ts, Button.tsx, or Button/index.tsx in the ./components directory. The bundler mode matches Vite's behavior exactly.
What Does the Vite Config Need for TypeScript React?
Vite's vite.config.ts handles the build process. When you scaffold with --template react-ts, Vite auto-configures React and TypeScript support. Here is a minimal working config:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});
The react() plugin tells Vite how to transform JSX and enables Fast Refresh (instant component updates in the browser without losing state). You do not need anything else for basic React+TypeScript development. For advanced projects, you might add path aliases or custom asset handling, but most React teams stick with this simple config.
How Do You Run Your First TypeScript React App?
After scaffolding and installing dependencies, you have three main npm scripts:
npm run dev # Start the development server (hot reload)
npm run build # Compile TypeScript and bundle for production
npm run preview # Preview the production build locally
Run npm run dev and open http://localhost:5173 in your browser. You see your app. Edit src/App.tsx and save—TypeScript checks the file and Vite reloads the browser instantly. If you make a type error, the browser overlay shows it, and your terminal also prints the error.
Key Takeaways
- Use
npm create vite@latest <name> -- --template react-tsto scaffold a new React+TypeScript project in seconds. - TypeScript's
tsconfig.jsonconfigures the compiler; always enable"strict": truefor maximum type safety. - Set
"jsx": "react-jsx"for React 17+, which eliminates the need for React imports in JSX files. - Use
"moduleResolution": "bundler"so TypeScript and Vite agree on how to find imported files. - The
@vitejs/plugin-reactplugin enables Fast Refresh and JSX/TypeScript support in one line. npm run devstarts a local dev server with instant reloading;npm run buildproduces a production bundle.
Frequently Asked Questions
Do I have to use Vite?
No. Create React App, Next.js, Remix, and other frameworks also support TypeScript. Vite is the fastest scaffolding option and has excellent developer experience, but any build tool that supports TypeScript works. Choose based on your project's needs.
Can I add TypeScript to an existing React project?
Yes. Add typescript and @types/react to npm, create a tsconfig.json in your root, and rename .jsx files to .tsx. TypeScript will start type-checking immediately. You can migrate gradually, keeping some files as .js and others as .ts until all code is typed.
What does strict mode do?
"strict": true enforces several rules: variables must have defined types, null checks are mandatory, and any is forbidden without explicit casting. It feels strict at first but saves hours of debugging by catching bugs the compiler can prevent. We recommend it for all new projects.
How do I use external npm packages with TypeScript?
If an npm package is a TypeScript package (includes .d.ts files), TypeScript automatically reads its types. For older JavaScript packages, install @types/<package-name> from the DefinitelyTyped registry. For example, npm install @types/react provides type definitions for React.
What is Fast Refresh?
Fast Refresh is a feature that reloads only the component you edited in the browser, preserving component state. Without it, a file change would hard-reload the page and lose state. Vite with the React plugin enables it automatically.