# Beyond the Web — 02. React 19 Features: use(), Actions, useOptimistic, useFormStatus, the React Compiler > **What / Why / How** — React 19 is mostly about *removing* code: less `useEffect`, less `useState`, less manual memoization. Learn the five APIs that replace them. --- ## 1. The Real Picture in 2026 `react@19.0` shipped December 2024. By mid-2026 it's the default in: - `next@15` (Next.js made the bump in October 2024) - `vite@5` + `@vitejs/plugin-react@4` - Expo SDK 52 and later - Most new project starters (`create-next-app`, `create-vite` React template, T3 stack) If you're on `next@14.x` or `react@18.x`, **most APIs in this file work in a backported form** — `useFormState` (renamed `useActionState` in 19), `useOptimistic`, `useFormStatus`, and `
` were already shipping in `react@18.3` and `next@14.3+`. The `use()` hook and the React Compiler are 19-specific. This file covers all of them with their stable 19 names, noting v18 fallbacks where relevant. --- ## 2. The Big Theme: Remove Effects, Remove State, Remove Memo A 2025 React team blog summed up React 19 as "the version that lets you delete code." Three concrete reductions: | Pattern in React 18 | What replaces it in React 19 | |---------------------|------------------------------| | `useEffect` to fetch data | `use(promise)` + Suspense, or Server Component `await` | | `useState` for "submitting / pending / error" around mutations | `useActionState` + Actions (`` directly) | | Manual `useMemo` / `useCallback` for stability | The **React Compiler** auto-memoizes | | Pessimistic UI ("set loading, await, set result") | `useOptimistic` shows the result immediately and rolls back on error | | `forwardRef` boilerplate | `ref` is a regular prop | You **don't have to** adopt all of them at once. They compose; pick the ones that fit your team's pace. --- ## 3. `use()` — Read a Promise (or Context) During Render ### What `use()` is the only React hook that can be called **conditionally**. It reads a promise or a context and suspends the component if the promise isn't resolved yet. ```tsx import { use } from 'react'; function UserName({ userPromise }: { userPromise: Promise }) { const user = use(userPromise); // suspends until the promise resolves return

{user.name}

; } // Wrap in Suspense to define the fallback: }> r.json())} /> ``` ### Why this beats `useEffect + useState` The pre-React-19 pattern: ```tsx // ❌ Old way — 8 lines of state machinery for one fetch function UserCard() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch('/api/me').then(r => r.json()).then(d => { setUser(d); setLoading(false); }).catch(e => { setError(e); setLoading(false); }); }, []); if (loading) return ; if (error) return ; return

{user!.name}

; } // ✅ New way — Suspense + use() function UserCard({ userPromise }: { userPromise: Promise }) { const user = use(userPromise); return

{user.name}

; } ``` The error UI moves to an `ErrorBoundary`, the loading UI moves to a ``. The component body shrinks to one line. ### How to actually create the promise — three real patterns **Pattern A — Server Component creates and passes down** ```tsx // app/me/page.tsx (Next.js 15 Server Component) import { Suspense } from 'react'; import { UserCard } from './UserCard'; export default function Page() { const userPromise = fetch('/api/me').then(r => r.json()) as Promise; return ( }> ); } ``` The Server Component creates the promise, **does not await it**, and passes it as a prop. The client `` calls `use(userPromise)` and suspends. Streaming SSR sends the skeleton first, then streams the resolved chunk. **Pattern B — TanStack Query's `suspense: true` mode** ```tsx function UserCard() { const { data } = useQuery({ queryKey: ['me'], queryFn: () => fetch('/api/me').then(r => r.json()), suspense: true, }); return

{data.name}

; } ``` Same `` boundary catches it. `data` is now non-nullable (TanStack v5 is suspense-aware). Covered in `04.state-and-data/03-data-fetching.md`. **Pattern C — Conditionally read context** ```tsx import { use, createContext } from 'react'; const ThemeCtx = createContext<'light' | 'dark' | null>(null); function MaybeThemed({ enabled }: { enabled: boolean }) { if (!enabled) return null; const theme = use(ThemeCtx); // ✅ legal — `use` allows conditional reads return
; } ``` `useContext` would error here ("hooks must be called in the same order every render"). `use` is explicitly designed for this. ### Pitfalls - **Don't create the promise inside the component that calls `use()`.** Each render creates a new promise → infinite suspend loop. Pass it down from a parent or stable cache (TanStack Query, `cache()`). - **Add `` and an ``.** Without them, an unresolved promise crashes upward. - **Don't conflate it with `useEffect`.** `use()` runs during render. Side effects still belong in actions or effects. --- ## 4. Actions — `` Directly Calls a Function ### What A "Server Action" is a function marked `'use server'` that React can call from a `` or a button. The framework handles serialization, network, response handling, and re-rendering. **You don't write `fetch()`.** This was introduced in `next@14.0` and is built into `react@19` itself. ```tsx // app/posts/actions.ts (Server Action — runs on the server) 'use server'; import { z } from 'zod'; import { revalidatePath } from 'next/cache'; const Schema = z.object({ title: z.string().min(1), body: z.string().min(1) }); export async function createPost(prev: unknown, formData: FormData) { const parsed = Schema.safeParse(Object.fromEntries(formData)); if (!parsed.success) return { error: parsed.error.flatten().fieldErrors }; await db.post.create({ data: parsed.data }); revalidatePath('/posts'); return { success: true }; } ``` ```tsx // app/posts/new/page.tsx (Server Component) import { createPost } from '../actions'; export default function NewPostPage() { return (