React 19 features promise to be game changers — even if it doesn’t have every feature we’ve hoped for.
From <form> hooks to React Server Components (RSC), React 19 is a testament to the continuous evolution.
The stable React 19 will come out around July, but you are most welcome to try out React 19 Beta by npm right now!
What Are The New React 19 Features?
Andrew Clark, a well-known React team developer, has teased some exciting changes coming by the end of 2024. Here’s a sneak peek:
- useMemo, useCallback, memo → React Compiler (Read more in my React Compiler post)
- forwardRef → Pass ref as a prop
- useContext → use(Context)
- throw promise → use(promise)
- <Context.Provider> → <Context>
Clearly, React 19 isn’t just about minor tweaks.
React 19 comes with new <form> actions and many new hooks:
- useActionState for better async handling
- useFormStatus for seamless form status integration
- useOptimistic for smoother state change
React 19 supports (finally) React Server Components (RSC) and native metadata tags — crucial for server- and client-side React applications. It also improves error reporting, with more detailed diffs for hydration errors (for more straightforward debugging).
React Compiler (formerly known as React Forget) is a separate project from React 19. While many popular libraries like SWR, React Query, and Next.js are already experimenting with it, the official release date is still open (official docs are on react.dev).
<form> Actions And New React Hooks
React version 19 arrives with a suite of new “action” hooks that make your web applications responsive and user-friendly!
Imagine your user desperately clicking the submit button. Without proper feedback, they question whether it froze or if the action even went through.
These new “actions” solve a common problem — they bring UI feedback while your async requests are pending (or failed) and keep the UI responsive.
useTransition
The useTransition hook allows updating a state without blocking the UI, enabling a more reactive app. This might become helpful when you have some async state updates that cause your interface to freeze.
const [isPending, startTransition] = useTransition();
useOptimistic
With useOptimistic, you can optimistically update the UI while you are waiting for server responses. This hook acts similarly to useState, but you can modify it while being in an async request.
const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);
useActionState
With the new hook called useActionState, you can control the action of the DOM <form> — it will make life with forms way easier. We can take care of form submission, handle asynchronous tasks, and set the initial state of the form!
This hook takes a function as the first parameter and returns a tuple with the form state and an action to bind the form to the state:
const [state, formAction] = useActionState(fn, initialState, permalink?);
useFormStatus
useFormStatus gets you status information about the last <form> submission, allowing you to provide valuable feedback to your users.
The hook gives you pending state (true/false), form data, HTTP request method (POST, GET, DELETE, etc), and the action itself.
const { pending, data, method, action } = useFormStatus();
In React 19, you can now pass actions directly to DOM <form>s, making form handling more intuitive:
<form action={actionFunction}>
Now, you can build a custom hook that wraps useFormStatus, useFormState, useOptimistic, and useTransition in one to address your specific needs.
use API/Hook for Async Scripts
Another intriguing React 19 addition is the new use
function — a fresh API to handle asynchronous resources during render.
While some might label it a hook, you can use it conditionally, which you can’t do with hooks.
“use” APIs work similarly to promises, managing async operations.
In general, when dealing with promises, we define functions with async keyword and consume them with await. use API abstracts from this model, enabling you to work with asynchronous resources WITHOUTH defining the functions as async or consuming them with await.
Here’s a simple example of how to call use API with React.Suspense (Read my 5 Real-World React.Suspence Examples for more info):
import React, { Suspense } from 'react'; import { use } from 'react'; // Simulate fetching data with a promise const fetchMessage = () => { return new Promise((resolve) => { setTimeout(() => { resolve("Data Fetched!"); }, 2000); }); }; const messagePromise = fetchMessage(); function MessageComponent() { // Use the use function to read the promise const message = use(messagePromise); return ( <div> <p>{message}</p> </div> ); } function App() { return ( <div> <h1>My App</h1> <Suspense fallback={<div>Loading...</div>}> <MessageComponent /> </Suspense> </div> ); } export default App;
The fetchMessage function returns a promise that resolves to “Data Fetched!” after 2 seconds.
In MessageComponent, we call use function to read the promise result. React suspendes the component until the promise resolves and shows a fallback UI (<div>Loading…</div>).
When the promise resolves, MessageComponent renders the “Data Fetched!” message.
Combining the use API with React.Suspense makes handling asynchronous data and displaying a fallback UI much easier.
Integrating React Server Components With Web Components
Server Components render ahead of time before bundling, allowing you to process data efficiently and preload content faster.
When you define a Server Action with the “use server” directive, React 19 marks it as accessible to the client, acting as an entry point, automatically creates a reference to the server function, and passes that reference to the Client Component.
When the client calls this function, React :
- Sends a request to the server
- Executes the function
- Returns the result
You can always call Client Components inside Server Components. Place server actions in separate files and invoke them in client components. This setup lets you fetch data using various patterns (including useEffect or new us API).
// serverActions.js 'use server'; export async function fetchData() { // Simulate data fetching return new Promise((resolve) => { setTimeout(() => { resolve("Data fetched from the server!"); }, 2000); }); } // ServerComponent.jsx import React, { Suspense } from 'react'; import { fetchData } from './serverActions'; import ClientComponent from './ClientComponent'; export default function ServerComponent() { const dataPromise = fetchData(); return ( <div> <h1>Server Component</h1> <Suspense fallback={<div>Loading...</div>}> <ClientComponent dataPromise={dataPromise} /> </Suspense> </div> ); } // ClientComponent.jsx import React from 'react'; import { use } from 'react'; export default function ClientComponent({ dataPromise }) { const data = use(dataPromise); return ( <div> <h2>Client Component</h2> <p>{data}</p> </div> ); }
Server actions introduce a different mindset, allowing seamless server-side operations from a client call with minimal ‘magic.’ Query the database directly from your client and avoid exposing database credentials — consistent results whether you call it from the client or server.
While server actions may initially seem complex, they offer benefits if you are heavily reliant on SSR.
As more sites embrace SSR, the advantages of server actions will become apparent — efficient data fetching and responsive UX (user experience) in apps.
Ref as Prop – Future of React
Say goodbye to forwardRef — React 19 allows you to use ref directly as a prop in function components. This means cleaner, more intuitive React code (and fewer headaches for developers).
forwardRef was a necessary evil in React. It allowed you to feed function components with refs and pass them down to children. It often felt cumbersome and counterintuitive.
Starting with React 19, you can now directly pass ref as a prop to function components. Here’s how it looks:
function MyInput({ placeholder, ref }) { return <input placeholder={placeholder} ref={ref} />; } // Usage <MyInput ref={ref} />
React even plans to release a codemod to automatically update your components to use the new ref prop and deprecate (and eventually remove) forwardRef in future versions.
Also, in React 19 you can reset the ref when the element is removed from the DOM with new cleanup syntax, similar to useEffect cleanup.
<input ref={(ref) => { // Ref created // NEW: return a cleanup function to reset // the ref when the element is removed from the DOM. return () => { // Ref cleanup }; }} />
When the component unmounts, React will call the cleanup function from the ref callback.
Custom Elements Support For Document Metadata And Stylesheets
We traditionally reserve <title>, <link>, and <meta> metadata for the <head> section of the document.
However, in React, we usually put components that determine metadata far from where you render the <head>. In some cases, React does not render the <head> at all.
Historically, devs inserted these elements manually or with React-Helmet, especially for SSR.
But React 19 brings native support of document metadata tags directly within your components.
Let’s take a look at how this works:
function BlogPost({ post }) { return ( <article> <h1>{post.title}</h1> <title>{post.title}</title> <meta name="author" content="Josh" /> <link rel="author" href="https://twitter.com/joshcstory/" /> <meta name="keywords" content={post.keywords} /> <p>Eee equals em-see-squared...</p> </article> ); }
When React renders this component, it recognizes the <title>, <link>, and <meta> tags and automatically hoists them to the <head> section of the document.
This native support ensures these tags function correctly across different environments, including client-only apps, streaming SSR, and Server Components.
You can also provide the precedence prop to both externally (<link rel=”stylesheet” href=”…”>) and inline (<style>…</style>) stylesheets in React 19 and control the order in which React insert them into DOM.
import React from 'react'; import { Stylesheet } from 'react'; function App() { return ( <div> <Stylesheet href="https://example.com/style1.css" precedence={1} /> <Stylesheet href="https://example.com/style2.css" precedence={2} /> <Stylesheet href="https://example.com/style3.css" precedence={3} /> <div className="styled-content"> <h1>Hello, World!</h1> <p>This content will be styled according to the precedence order.</p> </div> </div> ); } export default App;
New In React 19: <Context> instead of <Context.Provider>
React 19 simplifies the context state management — say goodbye to <Context.Provider>!
You can now use <Context> directly as the provider. Wrap your components with it instead of <Context.Provider> to pass down values.
Old syntax:
const MyContext = createContext(''); function App() { return ( <MyContext.Provider value={someValue}> <MyComponent /> </MyContext.Provider> ); }
New syntax:
const MyContext = createContext(''); function App() { return ( <MyContext value={someValue}> <MyComponent /> </MyContext> ); }
React 19 removes the “.Provider” suffix, reducing the code boilerplate and making your code easier to read!
To refactor your components, simply replace instances of <Context.Provider> with <Context>.
Meta/Facebook Impact on React Developers
Meta (formerly Facebook) has a unique approach to open-source development process. They create tools internally, rigorously test them within their infrastructure, and only then release them to the public.
This top-down method ensures stability and robustness but often stifles React ecosystem innovation and limits the community’s ability to experiment and develop their own solutions.
Wouldn’t it be better to let the community play with new ideas and approaches before Meta swoops in with its solutions?
React community has proven that it can build incredible alternatives and should continue pushing boundaries and exploring new ideas.
After all, it’s this spirit of experimentation and evolution that keeps React at the forefront of web development.
Check React Canary channel for pre-release tracks and release candidates.
However, not all Canary features will make it into React 19…
…Some anticipated features, such as full TypeScript integration, ESM support, and tree shaking, are absent, which is a bit disappointing.
Additionally, the absence of useEffectEvent is a letdown. This hook addresses issues with useEffect by decoupling reactivity from non-reactive code for a more predictable DX (developer experience).
React 19 vs Next.js and Remix: Navigating the Future With React Compiler
With the buzz around React 19, featuring React Compiler, server actions, and directives like “use client” and “use server,” many are wondering how this will impact frameworks like Next.js and Remix.
React 19 introduces significant optimization, but it doesn’t eliminate the need for frameworks like Next.js or Remix. Despite the overlap, Next and Remix offer more than just server-side rendering (SSR).
These frameworks serve webpages with initial data using HTML templates but also integrate React to fetch or send data post-render. Next.js or Remix simplifies this process and reduces boilerplate code, allowing you to focus on building apps.
The only question I am concerned about is the compatibility of Next.Js Turbo Compiler with React Compiler. There’s no definitive answer, yet I’m awaiting updates. I anticipate that both new tools will enhance Next.js capabilities!
React 19 vs Svelte
It’s amusing to see posts about how Svelte is becoming more like React, just as React is adopting features from Svelte. Have they ever considered collaborating…
…After all, we all share a goal of building better tools!
Each framework learns from the other’s mistakes and innovations. Yet, we often frame this as a battle. The truth is that every framework and library will continue to evolve, learning from each other indefinitely.
At the end of the day, it’s all just JavaScript. Use what works best for you and your project — it’s the same language under the hood.
Svelte’s biggest advantage is its syntax and performance. A typical Svelte app’s codebase is roughly 30-40% smaller than a React app with equivalent functionality.
React’s strength lies in its native data-based approach. You don’t have to worry about whether to use Writable<{ foo: string, bar: number }> or { foo: Writable<string>, bar: Writable<number> }.
In React, it’s simply { foo: string, bar: number }. Anyone who has worked on a complex Svelte app knows this frustration (pain).
React Compiler resolves some of its pain points and is a great addition! But it can’t fully replicate Svelte a compiler-first approach, processing components at build time rather than runtime.
Personally, I think React is in a solid place right now. The new Compiler (formerly React Forget) sounds promising, though it isn’t as mind-blowing as hooks were.
Conclusion: React 19 New Features
React 19 brings a host of exciting new features — form hooks, the ‘use’ hook, advanced ref handling, React Server Components, and many others.
Whether building a small project or a large-scale application, these new features in React 19 will help you create more robust and efficient code.