React 19 New Features: use API, Preload API, Actions, Optimistic Updates, and React Compiler
This article introduces the new React 19 APIs—including the versatile use API for data fetching and conditional context, resource preloading helpers, enhanced Actions with useTransition, useActionState, useFormState, useOptimistic, as well as deprecations, improved hydrate error messages, and the experimental React Compiler—providing code examples and migration guidance.
As of today the React team has published a Release Candidate for version 19.0.0 on NPM, bringing several previously unseen features and simplifying or removing many criticized APIs.
New use API
React 19 introduces a multipurpose use API with two main uses: fetching data during component render and conditionally reading a context.
Asynchronous data fetching
The use API accepts a Promise and suspends rendering until the promise is fulfilled, typically used together with Suspense . Previously this required a combination of useState , useEffect , and manual state updates.
import { useState, useEffect } from 'react';
import './App.css';
function getPerson(): Promise<{ name: string }> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: '19Qingfeng' });
}, 1000);
});
}
const personPromise = getPerson();
function App() {
const [loading, setLoading] = useState(false);
const [name, setName] = useState
();
useEffect(() => {
setLoading(true);
personPromise.then(({ name }) => {
setName(name);
setLoading(false);
});
}, []);
return (
Hello:
{loading ? 'loading' :
userName: {name}
}
);
}
export default App;With the new use API the same logic becomes much shorter:
import { use, Suspense } from 'react';
import './App.css';
function getPerson(): Promise<{ name: string }> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: '19Qingfeng' });
}, 1000);
});
}
const personPromise = getPerson();
function Person() {
const person = use(personPromise);
return
userName: {person.name}
;
}
function App() {
return (
Hello:
Loading...
}>
);
}
export default App;Conditional Context reading
Before React 19, useContext had to be called unconditionally at the top of a component. The new use API allows conditional context access while still respecting the rule that hooks must be called inside the render function.
import { use } from 'react';
import ThemeContext from './ThemeContext';
function Heading({ children }) {
if (children == null) {
return null;
}
const theme = use(ThemeContext);
return
{children}
;
}Resource Preloading API
React 19 adds helpers such as prefetchDNS , preconnect , preload , and preinit to hint the browser about resources that should be fetched early, improving page performance.
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';
function MyComponent() {
preinit('https://.../path/to/some/script.js', { as: 'script' });
preload('https://.../path/to/font.woff', { as: 'font' });
preload('https://.../path/to/stylesheet.css', { as: 'style' });
prefetchDNS('https://...');
preconnect('https://...');
}Actions and useTransition
Actions simplify async form handling. With useTransition , the pending state is managed automatically.
import { useState, useTransition } from 'react';
function updateName(name) {
return new Promise((resolve) => {
setTimeout(() => resolve(undefined), 1000);
});
}
export default function UpdateName() {
const [name, setName] = useState('');
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
console.log('Form submitted');
});
};
return (
setName(e.target.value)} />
Update
{error &&
{error}
}
);
}useActionState
useActionState returns a wrapped action function, its state, and a pending flag, allowing concise form submission handling.
import { useActionState } from 'react';
let index = 0;
function updateName(name) {
return new Promise((resolve, reject) => {
setTimeout(() => {
index++ === 0 ? resolve('Form updated') : reject();
}, 200);
});
}
export default function ChangeName() {
const [state, submitAction, isPending] = useActionState(
async (prev, formData) => {
try {
const result = await updateName(formData.get('name'));
return result;
} catch (e) {
return e;
}
},
null
);
return (
Update
{state}
);
}useFormStatus (formerly useFormState )
This hook provides the pending state and other form metadata directly inside form children.
import { useFormStatus } from 'react-dom';
function DesignButton() {
const { pending } = useFormStatus();
return
;
}useOptimistic
Optimistic updates let the UI reflect changes immediately while the server request is in flight, rolling back on failure.
// Thread.tsx
import { useOptimistic, useRef } from 'react';
export async function deliverMessage(message) {
await new Promise((res) => setTimeout(res, 1000));
return message;
}
export function Thread({ messages, sendMessage }) {
const formRef = useRef();
async function formAction(formData) {
addOptimisticMessage(formData.get('message'));
formRef.current.reset();
await sendMessage(formData);
}
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [...state, { text: newMessage, sending: true }]
);
return (
<>
{optimisticMessages.map((msg, i) => (
{msg.text}{msg.sending &&
(Sending...)
}
))}
Send
);
}
// main.tsx
import { Thread, deliverMessage } from './actions/happy.tsx';
import './index.css';
function App() {
const [messages, setMessages] = useState([
{ text: 'Hello there!', sending: false, key: 1 }
]);
async function sendMessage(formData) {
try {
const sentMessage = await deliverMessage(formData.get('message'));
setMessages((msgs) => [...msgs, { text: sentMessage }]);
} catch (e) {
console.error(e);
}
}
return
;
}
ReactDOM.createRoot(document.getElementById('root')!).render(
);Other Improvements
forwardRef deprecation: In React 19 the forwardRef API is slated for removal; refs can be passed as regular props in function components (but not in class components).
Better hydrate error messages: Server‑side rendering now reports a single concise mismatch message to aid debugging.
Direct Context rendering: A <Context> element can now act as a provider without the explicit <Context.Provider> wrapper.
Cleanable refs: Ref callbacks may return a cleanup function that runs when the element is removed from the DOM.
React Compiler (experimental)
The React Compiler automatically memoizes component code at compile time, reducing the need for manual useMemo or useCallback usage. While still experimental, it can significantly improve performance by generating optimized render functions.
// before
const Component = () => {
const onSubmit = () => {};
const onMount = () => {};
useEffect(() => {
onMount();
}, [onMount]);
return
;
};
// after (compiler adds memoization)
const FormMemo = React.memo(Form);
const Component = () => {
const onSubmit = useCallback(() => {}, []);
const onMount = useCallback(() => {}, []);
useEffect(() => {
onMount();
}, [onMount]);
return
;
};For more details, see the official React Compiler documentation.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.