Believemy logo purple

What's new in React 19

React 19 has just been announced by the Meta team. Powerful new features have been introduced almost two years after the release of React 18.3. Let's find out what's new with React 19.
Updated on December 5, 2024
Believemy logo

Summary

1. The new use function 💫

    1.1 Example 1: With a Promise

    1.2 Example 2: With a Context

2. What's new for refs 👀

3. The new hook useOptimistic()

4. Meta tag support 👑

5. Client Components and Server Components

    5.1 Client Components

    5.2 Server Components

        5.2.1 Using a Server Component

        5.2.2 Example

6. Server Actions 📝

    6.1 Without Server Actions

    6.2 With 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.

JSX
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:

JSX
import { use } from 'react';

const Message = ({ promiseForMessage }) => {
  const message = use(promiseForMessage);

  return (
    <div className="message">
      {message.text}
    </div>
  )
}

And for the promise:

JSX
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.

JSX
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:

JSX
import { forwardRef } from "react";

const Button = forwardRef((props, ref) => (
  <button ref={ref}>
    {props.children}
  </button>
));

Now, here's how you can use refs:

JSX
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 value
  • updateOptimistic - the function that allows you to change the value
JSX
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:

For example, here's how you can customize meta tags with React 19:

JSX
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.

Example of how a client component works
Example of how a Client Component works

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:

  1. Page loading times depend on the user's connection as well as their device.
  2. Extended delays reduce signals for the Web Core Vitals and thus the page's search engine ranking.
  3. 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:

Example of how a server component works
Example of how a Server Component works

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:

  1. 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.
  2. 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.
  3. 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:

  1. Cannot use Hooks like useState, useEffect.
  2. Cannot access browser-integrated functionalities like local storage.
  3. 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.

JSX
'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:

JSX
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:

JSX
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 of onSubmit
  • 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:

JSX
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 object
  • method - To retrieve the HTTP method
  • action - To execute a function passed by reference

You can also use a simplified version with the following code:

JSX
const { status } = useFormStatus();

Here's a concrete example:

JSX
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:

JSX
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 button
  • isPending - 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 submitted
  • 0 - our initial state

Here's an example:

JSX
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:

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.

React Training
Home page of our React course

 

Category: Development
Believemy logo
Comments (0)

You need to be logged in to comment on this article: log in or sign up.

Try for Free

Whether you're scaling your startup, building your first website, or transitioning to a developer role, Believemy is your new home. Join us, grow, and let’s build your project together.

Believemy is with anyone