React 19 Key Changes
Actions (async transitions), React Compiler (auto memoization), improved ref handling.
Async Actions
function LikeButton({ postId }) {
const [liked, setLiked] = useState(false)
const [isPending, startTransition] = useTransition()
function handleLike() {
startTransition(async () => {
await toggleLike(postId)
setLiked(prev => !prev)
})
}
return <button onClick={handleLike} disabled={isPending}>{liked ? 'Liked' : 'Like'}</button>
}
useActionState
async function submit(prevState, formData) {
try {
await api.update({ name: formData.get('name') })
return { ok: true, msg: 'Saved!' }
} catch (e) {
return { ok: false, msg: e.message }
}
}
function Form() {
const [state, action, pending] = useActionState(submit, {})
return (
<form action={action}>
{state?.msg && <p>{state.msg}</p>}
<input name="name" />
<button disabled={pending}>{pending ? '...' : 'Save'}</button>
</form>
)
}
useOptimistic
const [list, addOptimistic] = useOptimistic(todos, (s, n) => [...s, n])
async function add(text) {
addOptimistic({ id: 'temp', text, done: false })
await api.createTodo(text)
}
use() Hook
function Profile({ userPromise }) {
const user = use(userPromise) // Suspends until resolved
return <h1>{user.name}</h1>
}
React Compiler: Auto Memoization
The compiler auto-inserts useMemo/useCallback — no manual optimization needed.
ref as Prop
function Input({ ref, ...props }) { return <input ref={ref} {...props} /> }
-> Debug state with the JSON Viewer.