bugl
bugl
HomeLearnPatternsPathsSearchPremium
HomeLearnPatternsPaths
Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript with React

Why Use TypeScript with React?

TypeScript enhances React with

  • Type safety for props, state, and context
  • Better IDE autocompletion and refactoring
  • Early error detection during development

Note

This tutorial assumes basic knowledge of React.

If you're new to React, consider checking out our React Tutorial first.

Getting Started

Create a new React + TypeScript app with Vite:

Example

npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

Your tsconfig.json should include these recommended compiler options:

Example

{
 "compilerOptions": {
 "target": "ES2020",
 "lib": ["ES2020", "DOM", "DOM.Iterable"],
 "module": "ESNext",
 "moduleResolution": "Node",
 "jsx": "react-jsx",
 "strict": true,
 "skipLibCheck": true,
 "noEmit": true,
 "resolveJsonModule": true,
 "allowSyntheticDefaultImports": true,
 "esModuleInterop": true,
 "forceConsistentCasingInFileNames": true
 },
 "include": ["src"]
}

Note

Keep strict enabled for best type safety.

The shown options work well with Vite and Create React App.

Component Typing

Define props with TypeScript and use them in a functional component:

Example

// Greeting.tsx
type GreetingProps = {
 name: string;
 age?: number;
};
export function Greeting({ name, age }: GreetingProps) {
 return (
 <div>
 <h2>Hello, {name}!</h2>
 {age !== undefined && <p>You are {age} years old</p>}
 </div>
);
}

Type-Safe Events

Type event handlers for inputs and buttons:

Example

// Input change
 function NameInput() {
 function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
 console.log(e.target.value);
 }
 return <input onChange={handleChange} />;
 }
 // Button click
 function SaveButton() {
 function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
 e.preventDefault();
 }
 return <button onClick={handleClick}>Save</button>;
 }

Typing State with useState

Use explicit types for numbers, unions, and nullable values:

Example

const [count, setCount] = React.useState<number>(0);
const [status, setStatus] = React.useState<'idle' | 'loading' | 'error'>('idle');
type User = { id: string; name: string };
const [user, setUser] = React.useState<User | null>(null);

useRef with DOM Elements

Type refs to DOM nodes to access properties safely:

Example

function FocusInput() {
 const inputRef = React.useRef<HTMLInputElement>(null);
 return <input ref={inputRef} onFocus={() => inputRef.current?.select()} />;
 }

Children Typing

Accept children with the React.ReactNode type:

Example

type CardProps = { title: string; children?: React.ReactNode };
function Card({ title, children }: CardProps) {
 return (
 <div>
 <h2>{title}</h2>
 {children}
 </div>
);
}

Fetch Helpers with Generics

Example

async function fetchJson<T>(url: string): Promise<T> {
 const res = await fetch(url);
 if (!res.ok) throw new Error('Network error');
 return res.json() as Promise<T>;
}
// Usage inside an async function/component effect
async function loadPosts() {
 type Post = { id: number; title: string };
 const posts = await fetchJson<Post[]>("/api/posts");
 console.log(posts);
}

Minimal Context and Custom Hook

Provide a small, typed context and a helper hook:

Example

type Theme = 'light' | 'dark';
const ThemeContext = React.createContext<{ theme: Theme; toggle(): void } | null>(null);
function ThemeProvider({ children }: { children: React.ReactNode }) {
 const [theme, setTheme] = React.useState<Theme>('light');
 const value = { theme, toggle: () => setTheme(t => (t === 'light' ? 'dark' : 'light')) };
 return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}
function useTheme() {
 const ctx = React.useContext(ThemeContext);
 if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
 return ctx;
}

Vite TypeScript types: Add Vite's ambient types to avoid missing definitions.

Example

// src/vite-env.d.ts
/// <reference types="vite/client" />

Alternatively, add to tsconfig.json :

Example

{
 "compilerOptions": {
 "types": ["vite/client"]
 }
}

About React.FC: Prefer directly typed function components.

React.FC is optional; it implicitly adds children but isn't required.

Optional baseUrl and paths : These can simplify imports if supported by your bundler.

Example

{
 "compilerOptions": {
 "baseUrl": ".",
 "paths": {
 "@/*": ["src/*"]
 }
 }
}

Configure only if your tooling (e.g., Vite, tsconfig-paths) is set up for path aliases.

Previous

TypeScript with Node.js

Next

TypeScript Tooling

This chapter

Overview
40

Lessons

241m

Read time

1. TypeScript Tutorial2. TypeScript Introduction3. TypeScript Getting Started4. TypeScript Simple Types5. TypeScript Explicit Types and Inference6. TypeScript Special Types7. TypeScript Arrays8. TypeScript Tuples9. TypeScript Object Types10. TypeScript Enums11. TypeScript Type Aliases and Interfaces12. TypeScript Union Types13. TypeScript Functions14. TypeScript Casting15. TypeScript Classes16. TypeScript Basic Generics17. TypeScript Utility Types18. TypeScript Keyof19. TypeScript Null & Undefined20. TypeScript Definitely Typed21. TypeScript 5.x Updates22. TypeScript Configuration23. TypeScript with Node.js24. TypeScript with React25. TypeScript Tooling26. TypeScript Advanced Types27. TypeScript Type Guards28. TypeScript Conditional Types29. TypeScript Mapped Types30. TypeScript Type Inference31. TypeScript Literal Types32. TypeScript Namespaces33. TypeScript Index Signatures34. TypeScript Declaration Merging35. TypeScript Async Programming36. TypeScript Decorators37. TypeScript in JavaScript Projects (JSDoc)38. TypeScript Migration Guide39. TypeScript Error Handling40. TypeScript Best Practices

On this page

Why Use TypeScript with React?Getting StartedComponent TypingType-Safe EventsTyping State with useStateuseRef with DOM ElementsChildren TypingFetch Helpers with GenericsMinimal Context and Custom Hook