React 19 Development
React 19 introduces powerful new features for building modern web applications with enhanced server capabilities, improved form handling, and better performance.
Quick Start
Basic Component with Hooks
import { useState, useEffect } from "react";
function Counter() { const [count, setCount] = useState(0);
useEffect(() => { console.log("Count changed:", count); }, [count]);
return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
Custom Hook for API Data
import { useState, useEffect } from "react";
function useApiData(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null);
useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); const result = await response.json(); setData(result); } catch (err) { setError(err); } finally { setLoading(false); } };
fetchData();
}, [url]);
return { data, loading, error }; }
// Usage
function UserProfile({ userId }) {
const { data: user, loading, error } = useApiData(/api/users/${userId});
if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; if (!user) return <div>No user found</div>;
return <div>{user.name}</div>; }
React 19 New Features
Server Actions and Forms
// Server action "use server"; async function updateUser(formData) { const name = formData.get("name"); const email = formData.get("email");
// Database update logic here await db.users.update({ name, email });
return { success: true, message: "User updated successfully" }; }
// Client component with form function UserForm({ user }) { const [state, formAction, isPending] = useActionState(updateUser, null);
return ( <form action={formAction}> <input name="name" defaultValue={user.name} /> <input name="email" defaultValue={user.email} /> <button type="submit" disabled={isPending}> {isPending ? "Updating..." : "Update"} </button> {state?.message && <p>{state.message}</p>} </form> ); }
Form Status Hook
import { useFormStatus } from "react-dom";
function SubmitButton() { const { pending } = useFormStatus();
return ( <button type="submit" disabled={pending}> {pending ? "Submitting..." : "Submit"} </button> ); }
function ContactForm() { return ( <form action={submitContact}> <input name="name" placeholder="Your name" /> <input name="email" placeholder="Your email" /> <SubmitButton /> </form> ); }
Server Components
// Server Component (runs on server) async function BlogPost({ id }) { const post = await db.posts.find(id);
return ( <article> <h1>{post.title}</h1> <p>{post.content}</p> <CommentSection postId={post.id} /> </article> ); }
// Client Component for interactivity ("use client"); function CommentSection({ postId }) { const [comments, setComments] = useState([]); const [newComment, setNewComment] = useState("");
const addComment = async () => { // Client-side logic for adding comments };
return ( <div> <h3>Comments</h3> {/* Comment rendering and form */} </div> ); }
Common Patterns
State Management with Context
import { createContext, useContext, useState, useReducer } from "react";
// Create context const AuthContext = createContext();
// Auth provider component function AuthProvider({ children }) { const [user, setUser] = useState(null);
const login = async (credentials) => { // Login logic setUser(userData); };
const logout = () => { setUser(null); };
return ( <AuthContext.Provider value={{ user, login, logout }}> {children} </AuthContext.Provider> ); }
// Custom hook for consuming context function useAuth() { const context = useContext(AuthContext); if (!context) { throw new Error("useAuth must be used within AuthProvider"); } return context; }
// Usage in components function Header() { const { user, logout } = useAuth();
return ( <header> {user ? ( <div> <span>Welcome, {user.name}</span> <button onClick={logout}>Logout</button> </div> ) : ( <div>Please log in</div> )} </header> ); }
Data Fetching with Suspense
import { Suspense } from "react";
// Async component async function UserList() { const users = await fetchUsers(); // async function
return ( <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }
// Using Suspense boundary function App() { return ( <div> <h1>Users</h1> <Suspense fallback={<div>Loading users...</div>}> <UserList /> </Suspense> </div> ); }
Optimistic Updates
import { useState, useTransition } from "react";
function TodoList() { const [todos, setTodos] = useState(initialTodos); const [isPending, startTransition] = useTransition();
const addTodo = async (newTodo) => { // Optimistic update const optimisticId = Date.now(); startTransition(() => { setTodos((prev) => [ ...prev, { id: optimisticId, text: newTodo, status: "optimistic", }, ]); });
try {
const savedTodo = await saveTodo(newTodo);
// Replace optimistic todo with saved one
setTodos((prev) =>
prev.map((todo) => (todo.id === optimisticId ? savedTodo : todo)),
);
} catch (error) {
// Revert optimistic update on error
setTodos((prev) => prev.filter((todo) => todo.id !== optimisticId));
}
};
return ( <div> <ul> {todos.map((todo) => ( <li key={todo.id} style={{ opacity: todo.status === "optimistic" ? 0.5 : 1 }} > {todo.text} </li> ))} </ul> <button onClick={() => addTodo("New task")} disabled={isPending}> Add Todo </button> </div> ); }
Error Boundaries
import { Component } from "react";
class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { hasError: false, error: null }; }
static getDerivedStateFromError(error) { return { hasError: true, error }; }
componentDidCatch(error, errorInfo) { console.error("Error caught by boundary:", error, errorInfo); }
render() { if (this.state.hasError) { return ( <div> <h2>Something went wrong</h2> <details>{this.state.error?.message}</details> </div> ); }
return this.props.children;
} }
// Usage function App() { return ( <ErrorBoundary> <MyComponent /> </ErrorBoundary> ); }
Performance Patterns
Memoization with useMemo and useCallback
import { useState, useMemo, useCallback } from "react";
function ExpensiveComponent({ data, onItemClick }) { const [filter, setFilter] = useState("");
// Memoize expensive computation const filteredData = useMemo(() => { console.log("Filtering data..."); return data.filter((item) => item.name.toLowerCase().includes(filter.toLowerCase()), ); }, [data, filter]);
// Memoize event handler const handleItemClick = useCallback( (item) => { onItemClick(item); }, [onItemClick], );
return ( <div> <input value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="Filter items..." /> <ul> {filteredData.map((item) => ( <li key={item.id} onClick={() => handleItemClick(item)}> {item.name} </li> ))} </ul> </div> ); }
Code Splitting with lazy loading
import { lazy, Suspense } from "react";
// Lazy load component const AdminDashboard = lazy(() => import("./AdminDashboard"));
function App() { const [isAdmin, setIsAdmin] = useState(false);
return ( <div> <h1>My App</h1> <button onClick={() => setIsAdmin(true)}>Admin View</button>
{isAdmin && (
<Suspense fallback={<div>Loading admin dashboard...</div>}>
<AdminDashboard />
</Suspense>
)}
</div>
); }
Best Practices
Component Structure
// Good: Single responsibility components
function UserAvatar({ user, size = "medium" }) {
return (
<img
src={user.avatar}
alt={user.name}
className={avatar avatar-${size}}
/>
);
}
function UserCard({ user }) { return ( <div className="user-card"> <UserAvatar user={user} size="large" /> <h3>{user.name}</h3> <p>{user.email}</p> </div> ); }
Prop Types and Default Values
import PropTypes from "prop-types";
function Button({
children,
variant = "primary",
size = "medium",
onClick,
disabled = false,
}) {
const className = btn btn-${variant} btn-${size};
return ( <button className={className} onClick={onClick} disabled={disabled}> {children} </button> ); }
Button.propTypes = { children: PropTypes.node.isRequired, variant: PropTypes.oneOf(["primary", "secondary", "danger"]), size: PropTypes.oneOf(["small", "medium", "large"]), onClick: PropTypes.func, disabled: PropTypes.bool, };
Requirements
-
React 19.0 or higher
-
Node.js 18+ (for development)
-
Modern browser with ES6+ support
Dependencies for Full React 19 Experience
npm install react@19 react-dom@19
Recommended Development Tools
npm install --save-dev @types/react @types/react-dom
Common Additional Libraries
-
State Management: zustand , jotai , or Context API
-
Routing: react-router-dom
-
Data Fetching: @tanstack/react-query
-
Form Handling: Built-in React 19 forms or react-hook-form
-
Styling: tailwindcss , styled-components , or CSS modules