React 19 New Features — Complete Guide
Advertisement
React 19 New Features — Complete Guide
React 19 introduces powerful features for forms, async operations, and component development, making state management and data handling more ergonomic.
- React 19 New Features — Complete Guide
- useFormStatus Hook
- useFormState Hook
- Enhanced Hooks
- useOptimistic
- useTransition Enhancements
- Automatic Batching
- Server Components Integration
- React Compiler
- Actions
- Ref Callbacks
- Enhanced Context
- Real-World: Complete Form
- FAQ
useFormStatus Hook
Track form submission state:
'use client'
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button disabled={pending} type="submit">
{pending ? 'Submitting...' : 'Submit'}
</button>
)
}
export function LoginForm() {
return (
<form action={loginAction}>
<input name="email" type="email" required />
<input name="password" type="password" required />
<SubmitButton />
</form>
)
}
useFormState Hook
Manage form state and actions:
'use client'
import { useFormState } from 'react-dom'
async function submitForm(previousState, formData) {
const result = await fetch('/api/submit', {
method: 'POST',
body: formData
})
return result.json()
}
export function MyForm() {
const [state, formAction] = useFormState(submitForm, null)
return (
<form action={formAction}>
<input name="username" />
{state?.error && <p>{state.error}</p>}
{state?.success && <p>Submitted!</p>}
<button type="submit">Submit</button>
</form>
)
}
Enhanced Hooks
useOptimistic
'use client'
import { useOptimistic } from 'react'
export function TodoList() {
const [todos, dispatch] = useReducer(todoReducer, initialTodos)
const [optimisticTodos, addOptimisticTodo] = useOptimistic(todos)
async function handleAddTodo(formData) {
const newTodo = { id: Math.random(), text: formData.get('todo') }
addOptimisticTodo(newTodo)
await fetch('/api/todos', {
method: 'POST',
body: JSON.stringify(newTodo)
})
}
return (
<div>
{optimisticTodos.map(todo => <div key={todo.id}>{todo.text}</div>)}
</div>
)
}
useTransition Enhancements
'use client'
import { useTransition } from 'react'
export function SearchResults() {
const [isPending, startTransition] = useTransition()
const [results, setResults] = useState([])
function handleSearch(query) {
startTransition(async () => {
const data = await fetch(`/api/search?q=${query}`)
.then(r => r.json())
setResults(data)
})
}
return (
<div>
{isPending && <p>Searching...</p>}
{results.map(result => <div key={result.id}>{result.title}</div>)}
</div>
)
}
Automatic Batching
React 19 batches updates automatically:
export function Counter() {
const [count, setCount] = useState(0)
// Both updates batch together, single render
function handleClick() {
setCount(c => c + 1)
setCount(c => c + 1)
}
return (
<div>
{count}
<button onClick={handleClick}>+2</button>
</div>
)
}
Server Components Integration
Improved Server Component handling:
// app/dashboard/page.tsx
async function DashboardData() {
const data = await fetch('/api/dashboard')
.then(r => r.json())
return <div>{data}</div>
}
export default function Dashboard() {
return (
<Suspense fallback={<div>Loading...</div>}>
<DashboardData />
</Suspense>
)
}
React Compiler
Automatic memoization:
function ExpensiveComponent({ data, config }) {
// React Compiler auto-memoizes without useMemo/useCallback
const filtered = data.filter(d => d.type === config.type)
return <List items={filtered} />
}
Actions
Server Actions with automatic loading:
// app/actions.ts
'use server'
export async function createItem(formData: FormData) {
const item = await db.items.create({
data: {
title: formData.get('title'),
description: formData.get('description')
}
})
return item
}
'use client'
import { createItem } from '@/app/actions'
import { useActionState } from 'react'
export function CreateForm() {
const [state, formAction, isPending] = useActionState(createItem, null)
return (
<form action={formAction}>
<input name="title" required />
<textarea name="description" required />
<button disabled={isPending}>
{isPending ? 'Creating...' : 'Create'}
</button>
{state?.error && <p>{state.error}</p>}
</form>
)
}
Ref Callbacks
Cleaner ref handling:
export function ImageGallery() {
const images = []
return (
<div>
{images.map(img => (
<img
key={img.id}
ref={(el) => {
if (el) console.log('Image rendered:', img.id)
}}
src={img.src}
/>
))}
</div>
)
}
Enhanced Context
Better context performance:
const ThemeContext = createContext()
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light')
// Automatically optimized
const value = { theme, setTheme }
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
)
}
Real-World: Complete Form
'use client'
import { useFormState, useFormStatus } from 'react-dom'
import { createPost } from '@/app/actions'
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button disabled={pending}>
{pending ? 'Publishing...' : 'Publish Post'}
</button>
)
}
export function CreatePostForm() {
const [state, formAction] = useFormState(createPost, null)
return (
<form action={formAction} className="space-y-4">
<div>
<label htmlFor="title">Title</label>
<input
id="title"
name="title"
required
className="w-full border p-2"
/>
{state?.fieldErrors?.title && (
<p className="text-red-600">{state.fieldErrors.title}</p>
)}
</div>
<div>
<label htmlFor="content">Content</label>
<textarea
id="content"
name="content"
required
className="w-full border p-2"
/>
{state?.fieldErrors?.content && (
<p className="text-red-600">{state.fieldErrors.content}</p>
)}
</div>
{state?.error && (
<p className="text-red-600">{state.error}</p>
)}
{state?.success && (
<p className="text-green-600">Post published!</p>
)}
<SubmitButton />
</form>
)
}
FAQ
Q: Do I need to update to React 19 immediately? A: No, React 18 is stable. Upgrade when your project needs new features.
Q: Are old hooks deprecated? A: No, all hooks remain. New hooks add convenience for common patterns.
Q: Does React 19 work with Next.js? A: Yes, create-next-app uses latest React by default.
React 19 makes building complex UIs more intuitive and performant.
Advertisement