On this page
TypeScript with React
React and TypeScript work together seamlessly. Typed props, state, and hooks catch UI bugs early and improve developer experience.
Create a Typed React Project
Use the TypeScript template with Vite:
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
This scaffolds .tsx files and a tsconfig.json configured for React.
Typing Components
Function Components
interface GreetingProps {
name: string;
count?: number;
}
function Greeting({ name, count = 0 }: GreetingProps) {
return (
<h1>Hello, {name}! Visits: {count}</h1>
);
}
export default Greeting;
Children
interface CardProps {
title: string;
children: React.ReactNode;
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
{children}
</div>
);
}
useState
TypeScript infers state from the initial value:
const [count, setCount] = useState(0); // number
const [name, setName] = useState(''); // string
Provide an explicit type when the initial value does not cover all cases:
interface User {
id: number;
name: string;
}
const [user, setUser] = useState<User | null>(null);
// Later:
setUser({ id: 1, name: 'Alice' });
useRef
DOM refs and mutable values:
import { useRef, useEffect } from 'react';
function FocusInput() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
Event Handlers
function LoginForm() {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="email" onChange={handleChange} />
<button type="submit">Log in</button>
</form>
);
}
Custom Hooks
import { useState, useEffect } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: string | null;
}
function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
Enable "jsx": "react-jsx" in tsconfig.json. Typed React components scale well in large codebases. In the final chapter, we apply TypeScript to Node.js and Express APIs.