React 19 brings exciting new features that improve performance, developer experience, and enable new use cases. Here's everything you need to know.
What's New in React 19?
React Compiler
The new React Compiler automatically optimizes your components.
javascript
// Before React 19 - manual memoization
const MemoizedComponent = memo(function Component({ a, b }) {
useEffect(() => {
console.log('Effect runs');
}, [a, b]);
return <div>{a} {b}</div>;
});
// With React 19 Compiler - automatic optimization
function Component({ a, b }) {
useEffect(() => {
console.log('Effect runs');
}, [a, b]);
return <div>{a} {b}</div>;
}Server Actions
Direct server mutations from client components.
javascript
// Define server action
'use server';
async function updateProfile(formData) {
const name = formData.get('name');
await db.user.update({ name });
revalidatePath('/profile');
}
// Use in client component
function ProfileForm() {
return (
<form action={updateProfile}>
<input name="name" type="text" />
<button type="submit">Update</button>
</form>
);
}use() Hook
Read resources in components and hooks.
javascript
import { use } from 'react';
function Comments({ commentsPromise }) {
// Suspend until promise resolves
const comments = use(commentsPromise);
return (
<ul>
{comments.map(comment => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
);
}
// Parent suspends while fetching
function Post({ id }) {
const commentsPromise = fetchComments(id);
return (
<Suspense fallback={<Loading />}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}Improved Server Components
Simplified Data Fetching
javascript
// Server Component
async function ProductList() {
// Direct database access
const products = await db.product.findMany();
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}Context in Server Components
javascript
// createContext.js
export const ThemeContext = createContext('light');
// Server Component can read context
async function Page() {
const theme = await getThemeFromCookie();
return (
<ThemeContext.Provider value={theme}>
<Content />
</ThemeContext.Provider>
);
}New Forms API
useActionState Hook
javascript
import { useActionState } from 'react';
function LoginForm() {
const [state, formAction, isPending] = useActionState(
loginAction,
{ error: null }
);
return (
<form action={formAction}>
<input name="email" type="email" />
<input name="password" type="password" />
{state.error && <div>{state.error}</div>}
<button disabled={isPending}>
{isPending ? 'Logging in...' : 'Login'}
</button>
</form>
);
}useFormStatus Hook
javascript
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Submitting...' : 'Submit'}
</button>
);
}
function MyForm() {
return (
<form action={submitForm}>
<input name="name" />
<SubmitButton />
</form>
);
}Enhanced Transitions
useTransition Improvements
javascript
function SearchResults() {
const [isPending, startTransition] = useTransition({
timeoutMs: 3000, // New: configurable timeout
});
const [results, setResults] = useState([]);
const handleSearch = (query) => {
startTransition(async () => {
const data = await searchAPI(query);
setResults(data);
});
};
return (
<div>
<input onChange={(e) => handleSearch(e.target.value)} />
{isPending && <Spinner />}
<ResultsList results={results} />
</div>
);
}Improved Error Handling
Error Boundary Enhancements
javascript
function ErrorBoundary({ children, fallback }) {
return (
<ErrorBoundary
fallback={<ErrorFallback />}
onError={(error, errorInfo) => {
// Log error
console.error('Error:', error);
// Send to error tracking
trackError(error, errorInfo);
}}
>
{children}
</ErrorBoundary>
);
}Performance Improvements
Automatic Memoization
javascript
// React 19 Compiler automatically optimizes
function ProductList({ products, filter }) {
const filtered = products.filter(p =>
p.name.includes(filter)
);
return (
<div>
{filtered.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}Improved Concurrent Rendering
javascript
function App() {
return (
<Suspense fallback={<Skeleton />}>
<ExpensiveComponent />
</Suspense>
);
}New Developer Tools
React DevTools Improvements
- Better Server Component support
- Timeline view for concurrent rendering
- Props and state diffing
- Component profiling
Bundle Size
React 19 maintains similar bundle size while adding features:
react: ~6KB gzipped
react-dom: ~24KB gzippedMigration Guide
From React 18 to 19
javascript
// Update package.json
{
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
// Update imports
// Before
import { render } from 'react-dom';
// After
import { createRoot } from 'react-dom/client';
// Before
render(<App />, document.getElementById('root'));
// After
const root = createRoot(document.getElementById('root'));
root.render(<App />);Breaking Changes
javascript
// Removed APIs
- ReactDOM.render (use createRoot)
- ReactDOM.unmountComponentAtNode (use root.unmount)
- ReactDOM.hydrate (use hydrateRoot)
// Updated APIs
- useEffect cleanup timing
- StrictMode behavior
- Error handlingBest Practices
DOs
- Leverage Server Components by default
- Use Server Actions for mutations
- Let the Compiler optimize your code
- Use Suspense for loading states
- Implement proper error boundaries
- Test concurrent features
DON'Ts
- Manually memoize everything (Compiler does it)
- Overuse Client Components
- Ignore Suspense boundaries
- Skip error handling
- Forget about accessibility
- Mix server and client logic incorrectly
Real-World Example
E-commerce Product Page
javascript
// Server Component
async function ProductPage({ id }) {
const product = await db.product.findUnique({ where: { id } });
return (
<div>
<h1>{product.name}</h1>
<ProductDetails product={product} />
<AddToCart productId={product.id} />
</div>
);
}
// Client Component for interactivity
'use client';
function AddToCart({ productId }) {
const [state, formAction] = useActionState(addToCart, null);
return (
<form action={formAction}>
<input type="hidden" name="productId" value={productId} />
<button type="submit">Add to Cart</button>
{state?.success && <div>Added!</div>}
</form>
);
}Conclusion
React 19 represents a significant step forward with the Compiler, Server Actions, and improved concurrent features. Focus on using Server Components by default, leverage the Compiler for optimization, and use the new hooks for better user experience. The future of React is concurrent and server-first!