← Back to articles

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:

  1. Actions — A new pattern for handling data mutations (forms, submissions, updates)
  2. New hooksuseActionState, useOptimistic, useFormStatus, and use()
  3. 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 directly
  • isPending: 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:

  • forwardRef is no longer needed (but still works)
  • defaultProps deprecated for function components (use default parameters)
  • String refs removed (use callback refs or createRef)
  • Legacy Context (contextTypes) removed
  • ReactDOM.render removed (use createRoot)

Codemods available:

npx @react-codemod/react-19 ./src

Adoption Strategy

  1. Upgrade React to 19 (fix any breaking changes)
  2. Start using useActionState for new forms (replace useState + try/catch)
  3. Add useOptimistic where you want instant UI feedback
  4. Replace forwardRef with ref-as-prop (gradual)
  5. 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:

  1. useActionState — Eliminates form boilerplate (use immediately)
  2. useOptimistic — Instant UI updates (high-impact UX improvement)
  3. ref as prop — No more forwardRef (quality of life)
  4. 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.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.