TypeScript for Frontend (React)
TypeScript for Frontend (React)
When you ask AI to build a frontend, it will almost always generate React with TypeScript. This tutorial covers the TypeScript patterns specific to React that you'll see in every AI-generated component.
React Component Files: .tsx
React components that contain JSX (the HTML-like syntax) use the .tsx file extension:
.ts— TypeScript without JSX.tsx— TypeScript with JSX (React components)
Component Props
Props are how you pass data to React components. TypeScript defines what props a component accepts:
// Define the props interface ButtonProps { label: string; onClick: () => void; variant?: "primary" | "secondary"; } // Use the props in a component const Button = ({ label, onClick, variant = "primary" }: ButtonProps) => { return ( <button className={variant} onClick={onClick}> {label} </button> ); }; // Using the component <Button label="Click me" onClick={() => console.log("clicked!")} /> <Button label="Cancel" onClick={handleCancel} variant="secondary" />
This is the #1 pattern you'll see. Every component has a Props interface defining what it accepts.
What to ask your AI: "Create a [Card / Modal / Form] component. Here are the props I need: [list them]."
Children Props
Some components wrap other content:
interface ContainerProps { children: React.ReactNode; // Can contain any JSX className?: string; } const Container = ({ children, className }: ContainerProps) => { return <div className={className}>{children}</div>; }; // Usage <Container className="my-class"> <h1>Hello</h1> <p>Any content here</p> </Container>
React.ReactNode means "anything that can be rendered" — text, elements, components, etc.
State with useState
useState is how React components manage data that changes. TypeScript types the state:
import { useState } from "react"; const Counter = () => { // TypeScript infers the type from the initial value const [count, setCount] = useState(0); // number const [name, setName] = useState(""); // string const [isOpen, setIsOpen] = useState(false); // boolean // Sometimes you need to specify the type explicitly const [user, setUser] = useState<User | null>(null); const [items, setItems] = useState<Product[]>([]); const [error, setError] = useState<string | null>(null); };
Key pattern: when the initial value is null or an empty array, TypeScript needs help knowing the full type:
useState<User | null>(null)— "this will be a User or null"useState<Product[]>([])— "this will be an array of Products"
What to ask your AI: "I need a component that manages a list of [items]. Set up the state with proper TypeScript types."
Event Handling
React events have TypeScript types. Here are the most common:
// Click event const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { console.log("Button clicked"); }; // Form submit const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); // process form... }; // Input change const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { setName(event.target.value); }; // Select change const handleSelect = (event: React.ChangeEvent<HTMLSelectElement>) => { setCategory(event.target.value); };
Don't memorize these. The pattern is always React.[EventType]<HTML[Element]Element>.
What to ask your AI: "What's the correct TypeScript type for an onChange handler on a textarea?"
Fetching Data with useEffect
A very common pattern — fetch data when a component loads:
import { useState, useEffect } from "react"; interface User { id: number; name: string; email: string; } const UserProfile = ({ userId }: { userId: number }) => { const [user, setUser] = useState<User | null>(null); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); useEffect(() => { const fetchUser = async () => { try { const response = await fetch(`/api/users/${userId}`); const data: User = await response.json(); setUser(data); } catch (err) { setError("Failed to load user"); } finally { setLoading(false); } }; fetchUser(); }, [userId]); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; if (!user) return null; return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); };
This pattern (loading/error/data states) appears in almost every component that fetches data.
What to ask your AI: "Create a component that fetches [data] from [endpoint] and displays it. Include loading and error states."
Common React + TypeScript Patterns
Form with Multiple Fields
interface FormData { name: string; email: string; message: string; } const ContactForm = () => { const [formData, setFormData] = useState<FormData>({ name: "", email: "", message: "", }); const handleChange = ( event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> ) => { const { name, value } = event.target; setFormData((prev) => ({ ...prev, [name]: value })); }; const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); // Submit formData... }; return ( <form onSubmit={handleSubmit}> <input name="name" value={formData.name} onChange={handleChange} /> <input name="email" value={formData.email} onChange={handleChange} /> <textarea name="message" value={formData.message} onChange={handleChange} /> <button type="submit">Send</button> </form> ); };
List Rendering
interface Todo { id: number; text: string; completed: boolean; } const TodoList = ({ todos }: { todos: Todo[] }) => { return ( <ul> {todos.map((todo) => ( <li key={todo.id} className={todo.completed ? "done" : ""}> {todo.text} </li> ))} </ul> ); };
Conditional Rendering
interface AlertProps { type: "success" | "error" | "warning"; message: string; onDismiss?: () => void; } const Alert = ({ type, message, onDismiss }: AlertProps) => { return ( <div className={`alert alert-${type}`}> <p>{message}</p> {onDismiss && <button onClick={onDismiss}>Dismiss</button>} </div> ); };
Prompts for Building Frontend with AI
Here are ready-to-use prompts for asking AI to build React components:
Layout & Pages:
- "Create a responsive navigation bar with links to Home, About, and Contact pages. Include a mobile hamburger menu."
- "Build a dashboard page with a sidebar, header, and main content area using TypeScript and Tailwind CSS."
Forms:
- "Build a signup form with name, email, password, and confirm password fields. Add validation that shows errors below each field."
- "Create a multi-step form wizard with 3 steps. Each step should validate before allowing the user to proceed."
Data Display:
- "Create a product listing page that fetches products from /api/products and displays them in a responsive grid of cards."
- "Build a data table component that supports sorting by column and pagination."
Interactive Components:
- "Build a modal component that opens when a button is clicked and closes when clicking outside or pressing Escape."
- "Create a search bar with debounced input that filters a list of items as the user types."
What's Next?
You've seen TypeScript for the frontend. The next tutorial covers TypeScript for Backend (Node.js) — API routes, database operations, and server-side patterns.