React 19 New Features Explained (2026): Everything You Need to Know
React 19 is the biggest React release in years. It introduces Actions, new hooks, a built-in form handling pattern, and completes the Server Components story. Here's what matters and how to use it.
The Big Ideas
React 19 focuses on three themes:
- Actions — A new pattern for handling data mutations (forms, submissions, updates)
- New hooks —
useActionState,useOptimistic,useFormStatus, anduse() - Server Components maturity — RSC is now a stable, first-class feature
Actions: The New Mental Model
Actions are async functions that handle data mutations. They replace the manual useState + try/catch + loading/error state pattern that every React developer has written hundreds of times.
Before (React 18)
function UpdateName() {
const [name, setName] = useState('')
const [error, setError] = useState(null)
const [isPending, setIsPending] = useState(false)
const handleSubmit = async () => {
setIsPending(true)
setError(null)
try {
await updateName(name)
} catch (err) {
setError(err.message)
} finally {
setIsPending(false)
}
}
return (
<form onSubmit={(e) => { e.preventDefault(); handleSubmit() }}>
<input value={name} onChange={(e) => setName(e.target.value)} />
<button disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
)
}
After (React 19)
function UpdateName() {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get('name'))
if (error) return error
return null
},
null
)
return (
<form action={submitAction}>
<input name="name" />
<button disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
)
}
What changed: Loading state, error handling, and form data extraction are all handled automatically. The action prop on <form> is a new React 19 feature.
New Hooks
useActionState
Replaces the loading/error/submission state boilerplate for any action.
const [state, formAction, isPending] = useActionState(actionFn, initialState)
state: Current return value of the action (error messages, success data, etc.)formAction: Pass to<form action={formAction}>or call directlyisPending: Boolean, true while the action is running
useOptimistic
Show optimistic UI updates before the server confirms.
function MessageList({ messages }) {
const [optimisticMessages, addOptimistic] = useOptimistic(
messages,
(currentMessages, newMessage) => [...currentMessages, newMessage]
)
async function sendMessage(formData) {
const message = formData.get('message')
addOptimistic({ text: message, sending: true })
await deliverMessage(message)
}
return (
<>
{optimisticMessages.map((msg) => (
<div key={msg.id} style={{ opacity: msg.sending ? 0.5 : 1 }}>
{msg.text}
</div>
))}
<form action={sendMessage}>
<input name="message" />
<button>Send</button>
</form>
</>
)
}
Why it matters: Optimistic UI is one of the biggest UX improvements you can make. React 19 makes it a one-liner instead of a complex state management pattern.
useFormStatus
Access the pending state of a parent form from any child component.
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus()
return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>
}
// Use anywhere inside a <form>
<form action={myAction}>
<input name="email" />
<SubmitButton />
</form>
Why it matters: No more prop drilling isPending to submit buttons. Any component inside a form can know if it's submitting.
use()
use() reads a resource (Promise or Context) during render. Unlike other hooks, it can be called conditionally.
// Read a promise
function UserProfile({ userPromise }) {
const user = use(userPromise) // Suspends until resolved
return <h1>{user.name}</h1>
}
// Read context (replaces useContext in some cases)
function Theme() {
const theme = use(ThemeContext)
return <div className={theme} />
}
Why it matters: use() + Suspense provides a clean pattern for data loading without useEffect or loading states.
Server Components (Stable)
React Server Components (RSC) are now stable in React 19. They render on the server, send HTML to the client, and never ship JavaScript to the browser.
Key Concepts
Server Components (default in frameworks like Next.js App Router):
- Run on the server only
- Can directly access databases, file systems, and APIs
- Don't increase client bundle size
- Can't use hooks, event handlers, or browser APIs
Client Components (marked with 'use client'):
- Run on both server (SSR) and client
- Can use hooks, state, and event handlers
- Ship JavaScript to the browser
// Server Component (default)
async function ProductPage({ id }) {
const product = await db.product.findUnique({ where: { id } })
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<AddToCartButton productId={id} /> {/* Client Component */}
</div>
)
}
// Client Component
'use client'
function AddToCartButton({ productId }) {
const [added, setAdded] = useState(false)
return (
<button onClick={() => { addToCart(productId); setAdded(true) }}>
{added ? 'Added ✓' : 'Add to Cart'}
</button>
)
}
Server Actions
Server Actions are functions that run on the server but can be called from client components. They replace API routes for mutations.
// actions.ts
'use server'
export async function createTodo(formData: FormData) {
const title = formData.get('title')
await db.todo.create({ data: { title } })
revalidatePath('/todos')
}
// Client Component
'use client'
import { createTodo } from './actions'
function AddTodo() {
return (
<form action={createTodo}>
<input name="title" />
<button>Add</button>
</form>
)
}
Other Improvements
ref as a Prop
No more forwardRef. In React 19, ref is a regular prop.
// Before (React 18)
const Input = forwardRef((props, ref) => <input ref={ref} {...props} />)
// After (React 19)
function Input({ ref, ...props }) {
return <input ref={ref} {...props} />
}
Document Metadata
Render <title>, <meta>, and <link> tags anywhere in your component tree. React hoists them to <head> automatically.
function BlogPost({ post }) {
return (
<>
<title>{post.title}</title>
<meta name="description" content={post.summary} />
<article>{post.content}</article>
</>
)
}
Improved Error Handling
Better error messages, onCaughtError and onUncaughtError callbacks on error boundaries, and cleaner stack traces.
Stylesheet Support
<link rel="stylesheet"> with a precedence prop is deduplicated and ordered correctly, even when rendered from multiple components.
Migration Guide
Upgrading from React 18
npm install react@19 react-dom@19
Breaking changes to watch for:
forwardRefis no longer needed (but still works)defaultPropsdeprecated for function components (use default parameters)- String refs removed (use callback refs or createRef)
- Legacy Context (
contextTypes) removed ReactDOM.renderremoved (usecreateRoot)
Codemods available:
npx @react-codemod/react-19 ./src
Adoption Strategy
- Upgrade React to 19 (fix any breaking changes)
- Start using
useActionStatefor new forms (replace useState + try/catch) - Add
useOptimisticwhere you want instant UI feedback - Replace
forwardRefwith ref-as-prop (gradual) - Adopt Server Components via your framework (Next.js App Router, etc.)
FAQ
Do I need a framework to use React 19?
For basic features (Actions, new hooks): no, React 19 works standalone. For Server Components and Server Actions: yes, you need a framework like Next.js that supports RSC.
Is React 19 production-ready?
Yes. It's the stable release. Major frameworks (Next.js 15+) fully support it.
Should I rewrite my existing app to use Actions?
No. Migrate gradually. Use Actions for new features and refactor existing code opportunistically. Your current patterns still work.
What happened to React Server Components?
RSC shipped as part of React 19 (previously experimental). They're now the recommended default in Next.js App Router and will be adopted by other frameworks.
Is useEffect still relevant?
Yes. useEffect handles side effects (subscriptions, DOM manipulation, external system synchronization). Actions handle data mutations. Different tools for different problems.
The Bottom Line
React 19's most impactful features for daily development:
- useActionState — Eliminates form boilerplate (use immediately)
- useOptimistic — Instant UI updates (high-impact UX improvement)
- ref as prop — No more forwardRef (quality of life)
- Server Components — Smaller bundles, direct data access (adopt via framework)
The common thread: React 19 removes boilerplate. Patterns that took 20 lines now take 3. Start using the new hooks in your next feature — no rewrite needed.