What's new in React 19
Summary
1. The new use function 💫
2. What's new for refs 👀
3. The new hook useOptimistic() ✅
4. Meta tag support 👑
5. Client Components and Server Components ✨
5.2.1 Using a Server Component
5.2.2 Example
6. Server Actions 📝
7. The new hooks useFormStatus() and useActionState() 🔗
7.1 How to use useFormStatus() ?
7.2 How to use useActionState() ?
8. The new compiler 🤖
9. Conclusion
The new use
function
This is a brand-new React Hook that's quite unique because it can be called conditionally without throwing an error. It allows you to read the value of a Context or a Promise.
const value = use(resources);
Example 1: With a Promise
When you want to use the use
function with a promise, you'll need to use Suspense while waiting for the request to resolve or reject. Here's an example with a promise that fetches a message:
import { use } from 'react';
const Message = ({ promiseForMessage }) => {
const message = use(promiseForMessage);
return (
<div className="message">
{message.text}
</div>
)
}
And for the promise:
import { Suspense } from 'react'
import Message from './Message'
function App() {
const promiseForMessage = fetch('https://jsonplaceholder.typicode.com/message').then((res) => res.json());
return (
<Suspense fallback={<div>Loading</div>}>
<Message promiseForMessage={promiseForMessage} />;
</Suspense>
)
}
export default App
Example 2: With a Context
You can also use the use
function with a context thanks to createContext. For instance, combining these two functions would look like this: you would simply pass your context to the use
function.
import { createContext, use } from 'react'
const IngredientsContext = createContext();
function Ingredients() {
const ingredients = use(IngredientsContext);
return (
<ul>
{ingredients.map((ingredient) => {
return <li key={ingredient}>{ingredient}</li>
})}
</ul>
)
}
export default function App() {
return (
<IngredientsContext value={['Chocolate', 'Kiwi', 'Flakes']}>
<Ingredients />
</IngredientsContext>
)
}
By the way, a small new feature in React 19 was used in this example: we didn't need to use IngredientsContext.Provider
. It's enough to use our context with its name IngredientsContext
.
What's New for Refs
React 19 also introduces new features for refs.
In practice, refs are now passed as props rather than having to use the React Hook forwardRef.
Before React 19, we had to use refs in this way if we wanted to pass a ref to a component:
import { forwardRef } from "react";
const Button = forwardRef((props, ref) => (
<button ref={ref}>
{props.children}
</button>
));
Now, here's how you can use refs:
const Button = ({ ref, children }) => (
<button ref={ref}>
{children}
</button>
));
The New Hook useOptimistic()
The useOptimistic Hook allows us to update the interface before the data is changed on the server, in order to reduce waiting time and increase the fluidity of requests.
For example, when a user posts a new message.
To do this, you need to create an element with useOptimistic
that takes the current value as a parameter and returns an object that contains:
optimisticValue
- the anticipated valueupdateOptimistic
- the function that allows you to change the value
import { useOptimistic, useState } from 'react';
const Messages = () => {
const [messages, setMessages] = useState([
{
id: 4,
content: "Hey",
pending: false
}
]);
const [optimisticMessages, newOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [
...state,
{
id: `newMessage-${messages.length}`,
content: newMessage,
pending: true
},
],
);
return (
<>
<div>
{optimisticMessages.map(message => (
<div
key={message.id}
style={{ background: message.pending ? "gray" : "transparent" }}
>
{message.content}
</div>
))}
</div>
<form action={async (data) => {
const newMessage = data.get('content');
newOptimisticMessage(newMessage);
await sendNewMessageToBackend(newMessage);
}}>
<input type="text" name="content" placeholder="Your new message" />
<button type="submit">Add my message</button>
</form>
</>
)
}
Now, when a user wants to add a message, the interface is updated optimistically (before the change is made in the database on the server).
As long as the message is being sent, we modify the style by changing the background color to light gray (with message.pending ? "gray" : "transparent"
in our example).
Once our message is sent with our sendNewMessageToBackend
function, we can imagine modifying the state of messages
, which also changes the state controlled by our useOptimisticValue
Hook, making the message have a transparent background to show that it has been successfully added.
Tip
We could also choose not to show the user that their message is being added if we didn't change the background color of the message. In this case, the user would have the impression that their message was added in one click.
Meta Tag Support
This new version 19 of React now provides access to certain meta tags such as:
- title
- meta tags
- description
For example, here's how you can customize meta tags with React 19:
const MainPage = () => {
return (
<>
<title>Believemy</title>
<meta name="description" content="Training to become a web developer" />
</>
);
}
Client Components and Server Components
Already present in the NextJS framework, they are finally officially arriving in React 19!
This concept is quite simple to understand: in the past, all components were loaded by the client, meaning by the user of a website. This resulted in long loading times for the user, which obviously reduced performance and, consequently, search engine ranking (for whom speed is an important metric in the Web Core Vitals).
Client Components
This is the type of components used by all components in a React project up to version 18.3. Here's how they work.

In Client Components, the user himself loads the bundle (all the dependencies used to load a page).
As you can see, the server only sends the resources to the user. It's the user who loads and uses the resources of their device to display the final page.
The problems with Client Components are quite numerous:
- Page loading times depend on the user's connection as well as their device.
- Extended delays reduce signals for the Web Core Vitals and thus the page's search engine ranking.
- The experience of waiting for a site to load degrades the user's desire and pleasure.
Fortunately, a solution has been found: the arrival of Server Components.
Server Components
This is the new type of components used with React 19. Let's analyze how Server Components work with this illustration:

In Server Components, the server itself loads the bundle and sends it ready for display to the user.
As you can see, the client merely receives the resources sent by the server. The server loads and uses its resources to display the final page.
Server Components have several advantages:
- The computing power used is that of the server, not the client's device, which improves the site's loading performance and thus its search engine ranking.
- Server Components have full access to back-end resources (such as the file system). The data is secured because it doesn't pass through the client.
- The client receives a minimal version of the bundle without the dependencies that were used to build the page.
Using a Server Component
To use a Server Component, you need to use the use server
directive. This is a common source of errors if you're familiar with Next.JS because, with Next.JS, all components are server components, unlike ReactJS where all components are client components by default.
However, there are some limitations with Server Components:
- Cannot use Hooks like useState, useEffect.
- Cannot access browser-integrated functionalities like local storage.
- Some Custom Hooks cannot be used if they depend on browser functionalities (some dependencies, therefore, do not work in a Server Component).
Example
Here's an example of using a Server Component in React 19.
'use server';
export default async function articles() {
const res = await fetch("https://api.example.com/articles");
const articles = res.json();
return (
<>
{articles.map(article => <div key={article.id}>{article.title}</div>)}
</>
)
};
This example is meant to be simple: we retrieve without using state with useState, for example, the articles directly within the component.
It's strange at first, liberating in the end. 🥰
Server Actions
Here, we take the same approach and start over: it's a function that will be executed on the server as soon as a form is submitted.
Without Server Actions
Before Server Actions, we had to detect the form submission and then call a method that would retrieve the entered data and make a request from the client side.
This has the disadvantage of being insecure because the user can see the request flow as well as the method contents.
For example, here's what we had without Server Actions:
const register = async (e) => {
// Etc
}
<form onSubmit={(e) => register(e)}>
<input name="pseudo" placeholder="Username" id="pseudo" />
<input name="password" placeholder="Password" id="password" />
<button type="submit">Create my account</button>
</form>
With Server Actions
With React 19, it is now possible to use Server Actions. As we saw, these allow us to execute a method on the server, thereby securing API calls and data.
Here's the same example with Server Actions:
const register = async (data) => {
'use server';
const newUser = {
pseudo: data.get('pseudo'),
password: data.get('password')
}
await fetch("...");
}
<form action={register}>
<input name="pseudo" placeholder="Username" id="pseudo" />
<input name="password" placeholder="Password" id="password" />
<button type="submit">Create my account</button>
</form>
A few things to note about Server Actions:
- The attribute used is
action
instead ofonSubmit
- You need to specify
use server
in the method you call to execute it on the server - The data is sent by React to the called method
The New Hooks useFormStatus() and useActionState()
To use Server Actions, React 19 introduces two new Hooks: useFormStatus and useActionState.
Each of these Hooks allows us to modify our code based on the actions used.
Let's analyze each one.
How to Use useFormStatus()?
This brand-new Hook works with React 19's Server Actions. It allows you to know the status of forms, the data sent, and the method used.
You can use this new Hook in the following way:
const { pending, data, method, action } = useFormStatus();
Here's what these different names correspond to:
pending
- To know the state of the form and whether it is being submitted (true
if yes,false
if no)data
- To retrieve the transmitted data as an objectmethod
- To retrieve the HTTP methodaction
- To execute a function passed by reference
You can also use a simplified version with the following code:
const { status } = useFormStatus();
Here's a concrete example:
import { useFormStatus } from "react";
function SubmitButton() {
const status = useFormStatus();
return (
<button disabled={status.pending}>
{status.pending ? "Loading..." : "Send"}
</button>
)
}
const serverAction = async () => {
"use server";
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for 5 seconds
}
export default const Form = () => {
return (
<form action={serverAction}>
<SubmitButton />
</form>
);
};
Here's what our small example does:
- We create a
SubmitButton
component whose text changes between "Loading..." and "Send" based on the form submission status. - We create a fake 5-second wait in serverAction so that the request lasts 5 seconds, allowing us to see the result of
useFormStatus()
. - We know when our form is still being submitted thanks to
status.pending
.
The useFormStatus
Hook is thus very useful for modifying the interface based on form submissions, for example.
How to Use useActionState()?
The advantage of the useActionState Hook is very intuitive: it allows you to modify the state based on the success or failure of an action.
If everything goes well? We update the state data.
If not? We display a message to indicate that an error occurred. You can use useActionState
in this way with React 19:
const [state, formAction, isPending] = useActionState(action, 0);
Here we have:
state
- Represents the initial state on the first render and the state updated by the action function passed as a parameter (you'll understand better in the example)formAction
- A new action that can be used in the formAction attribute of a buttonisPending
- To detect when an action is being performed or not (and thus to change the interface accordingly)
Finally, we pass to useActionState
:
action
- the method we want to use when the form is submitted0
- our initial state
Here's an example:
import { useActionState } from "react";
async function add(prevState, data) {
return prevState + 1;
}
export default function Form() {
const [state, formAction, isPending] = useActionState(add, 0);
return (
<form>
{state}
<button formAction={formAction}>Add</button>
</form>
)
}
The New Compiler
This is the biggest announcement in the version 19 of React JS: up to the previous version, developers used techniques to optimize their code using dedicated functions:
- useMemo
- useCallback
- memo
- etc...
This is now ancient history with React 19! 🥳
The brand-new React compiler now automatically detects and intelligently optimizes our code. This allows for significantly improved performance for the user experience and optimization for search engines.
Conclusion ✨
Here’s everything you need to know about the new features in React 19! This version brings a lot of new features and performance optimizations.
Nowadays, it is undeniably the best front-end library for creating websites.
If you want to go further and learn the latest features of React, take a look at our dedicated React course. We will update it with version 19 as soon as React officially releases this new version for production use.
