New client-side hooks coming to React 19
Tags: react
Published: 2024-01-23 12:04:11

---
title: New client-side hooks coming to React 19
published: true
description: Data fetching and form handling are about to get easier in React, and not just in SSR apps.
tags:
 - react
cover_image: https://marmelab.com/static/230d0856b739d1ae691d783fae0db0d5/01093/large.jpg
canonical_url: https://marmelab.com/blog/2024/01/23/react-19-new-hooks.html
---

Contrary to popular belief, the React core team isn't solely focused on React Server Components and Next.js. New client-side hooks are coming in the next major version of React, React 19. They focus on two major pain points in React: **data fetching** and **forms**. These hooks will enhance the productivity of all React developers, including those working on Single-Page Apps.

Without further ado, let's dive into the new hooks!

-   [`use(Promise)`](#usepromise)
-   [`use(Context)`](#usecontext)
-   [Form actions](#orm-actions)
-   [`useFormState`](#useformstate)
-   [`useFormStatus`](#useformstatus)
-   [`useOptimistic`](#useoptimistic)
-   [Bonus: Async transitions](#bonus-async-transitions)

**Note**: These hooks are only available in [React’s Canary and experimental channels](https://react.dev/community/versioning-policy#canary-channel). They should be part of the upcoming React 19, but the API may change before the final release.

## use(Promise)

This new hook is the official API for "suspending" on the client. You can pass it a promise, and React will suspend on it until it resolves. The basic syntax, taken from [the React `use` documentation](https://react.dev/reference/react/use), is:

```tsx
import { use } from 'react';

function MessageComponent({ messagePromise }) {
    const message = use(messagePromise);
    // ...
}
```

The great news is that this hook can be used for data fetching. Here is a concrete example with data fetching on mount and on click on a button. The code doesn't use a single `useEffect`:

{% embed https://stackblitz.com/edit/stackblitz-starters-2klrvg?ctl=1&embed=1&file=src%2FApp.tsx&hideDevTools=1&hideExplorer=1 %}

Remember [this warning in the `<Suspense>` documentation](https://react.dev/reference/react/Suspense#displaying-a-fallback-while-content-is-loading)?

> Suspense-enabled data fetching without the use of an opinionated framework is not yet supported.

Well, it's no longer true for React 19.

This new `use` hook has a hidden power: Unlike all other React Hooks, `use` can be called within loops and conditional statements like `if`.

Does that mean that we no longer need to use a third-party library like [TanStack Query](https://tanstack.com/query/latest) to fetch data on the client side? Well, it remains to be seen, as [Tanstack Query does more than just resolving a Promise](https://tkdodo.eu/blog/why-you-want-react-query).

But it's a great step in the right direction, and it will make it easier to build Single-page apps based on REST or GraphQL APIs. I'm super enthusiastic about this new hook!

Read more about [the `use(Promise)` hook](https://react.dev/reference/react/use) in the React documentation.

## use(Context)

The same `use` hook can be used to read a React Context. It's exactly like `useContext`, except it can be called within loops and conditional statements like `if`.

```tsx
import { use } from 'react';

function HorizontalRule({ show }) {
    if (show) {
        const theme = use(ThemeContext);
        return <hr className={theme} />;
    }
    return false;
}
```

This will simplify the component hierarchy for some use cases, as the only way to read a context in a loop or a conditional was to split the component in two.

It's also a great evolution in terms of performance, as you can now conditionally skip the re-rendering of a component, even though the context has changed.

Read more about [the `use(Context)` hook](https://react.dev/reference/react/use) in the React documentation.

## Form Actions

This new feature enables you to pass a function to the `action` prop of a `<form>`. React will call this function when the form is submitted:

```tsx
<form action={handleSubmit} />
```

Remember that if you add a `<form action>` prop in React 18, you get this warning:

> Warning: Invalid value for prop `action` on `<form>` tag. Either remove it from the element or pass a string or number value to keep it in the DOM.

That's no longer true in React 19, where you can write a form like this:

{% embed https://stackblitz.com/edit/stackblitz-starters-j6yogy?ctl=1&embed=1&file=src%2FApp.tsx&hideDevTools=1 %}

The `addToCart` function is _not_ a Server Action. It is called on the client side, and it can be an async function.

This will greatly simplify the handling of AJAX forms in React - like for instance for a search form. But again, this may not be enough to get rid of third-party libraries like [React Hook Form](https://react-hook-form.com/), which does much more than just handle form submission (validation, side effects, etc).

**Tip**: You may find some usability issues in the example above (submit button not disabled while submitting, missing confirmation message, late update of the cart). Fortunately, more hooks are coming to help with this use case. Read on!

You can read more about [the `<form action>` prop](https://react.dev/reference/react-dom/components/form) in the React documentation.

## useFormState

This new hook aims to help with the async form action feature described above. Call `useFormState` to access the return value of an action from the last time a form was submitted.

```tsx
import { useFormState } from 'react-dom';
import { action } from './action';

function MyComponent() {
    const [state, formAction] = useFormState(action, null);
    // ...
    return <form action={formAction}>{/* ... */}</form>;
}
```

For instance, this lets you display a confirmation or an error message returned by the form action:

{% embed https://stackblitz.com/edit/stackblitz-starters-ibotc3?ctl=1&embed=1&file=src%2FApp.tsx&hideDevTools=1&hideExplorer=1 %}

**Note**: `useFormState` must be imported from `react-dom`, not `react`.

You can read more about the [`useFormState`](https://react.dev/reference/react-dom/hooks/useFormState) hook in the React documentation.

## useFormStatus

`useFormStatus` lets you know if a parent `<form>` is currently submitting or has submitted successfully. It can be called from children of the form, and it returns an object with the following properties:

```tsx
const { pending, data, method, action } = useFormStatus();
```

You can use the `data` property to display what data is being submitted by the user. You can also display a pending state, as in the following example where the button is disabled while the form is submitting:

{% embed https://stackblitz.com/edit/stackblitz-starters-bbvrpk?ctl=1&embed=1&file=src%2FApp.tsx&hideDevTools=1&hideExplorer=1 %}

**Note**: `useFormState` must be imported from `react-dom`, not `react`. Also, it only works when the parent form uses [the `action` prop described above](#form-actions).

Together with `useFormState`, this hook will improve the user experience of client-side forms without cluttering your components with useless context of effects. You can read more about [the `useFormStatus` hook](https://react.dev/reference/react-dom/hooks/useFormStatus) in the React documentation.

## useOptimistic

This new hook lets you optimistically update the user interface while an action is being submitted.

```tsx
import { useOptimistic } from 'react';

function AppContainer() {
    const [optimisticState, addOptimistic] = useOptimistic(
        state,
        // updateFn
        (currentState, optimisticValue) => {
            // merge and return new state
            // with optimistic value
        },
    );
}
```

In the cart example above, we could use this hook to display the cart with the new item added before the AJAX call is finished:

{% embed https://stackblitz.com/edit/stackblitz-starters-6tyedh?ctl=1&embed=1&file=src%2FApp.tsx&hideDevTools=1&hideExplorer=1&hideNavigation=1 %}

Optimistically updating the UI is a great way to improve the user experience of a web app. This hooks helps a lot with this use case. You can read more about [the `useOptimistic` hook](https://react.dev/reference/react/useOptimistic) in the React documentation.

## Bonus: Async Transitions

React's Transition API lets you update the state without blocking the UI. For instance, it lets you cancel a previous state change if the user changes their mind.

The idea is to wrap a state change with a call to `startTransition`.

```tsx
function TabContainer() {
    const [isPending, startTransition] = useTransition();
    const [tab, setTab] = useState('about');

    function selectTab(nextTab) {
        // instead of setTab(nextTab), put the state change in a transition
        startTransition(() => {
            setTab(nextTab);
        });
    }
    // ...
}
```

The following example shows a tab navigation using this Transitions API. Click “Posts” and then immediately click “Contact”. Notice that this interrupts the slow render of “Posts”. The “Contact” tab shows immediately. Because this state update is marked as a transition, a slow re-render did not freeze the user interface.

{% embed https://stackblitz.com/edit/stackblitz-starters-qrc5hb?ctl=1&embed=1&file=src%2FApp.tsx&hideDevTools=1 %}

The `useTransition` hook is already available in React 18.2. What's new in React 19 is that you can now pass an async function to `startTransition`, which is awaited by React to start the transition.

This is useful for submitting data through an AJAX call and rendering the result in a transition. The transition pending state starts with the async data submission. It is already used in the [form actions](#form-actions) feature described above. This means that React calls the `<form action>` handler wrapped in `startTransition`, so it doesn’t block the current page.

This feature isn't yet documented in the React documentation, but you can read more about it in [the pull request](https://github.com/facebook/react/pull/26621).

## Conclusion

All of these features work in client-only React apps, for instance in apps bundled with Vite. You don't need an SSR framework like Next or Remix to use them - although they also work with server-integrated React apps.

With these features, data fetching and forms become significantly easier to implement in React. However, creating a great user experience involves integrating all these hooks, which can be complex. Alternatively, you can use a framework like [react-admin](https://marmelab.com/react-admin/) where user-friendly forms with optimistic updates are built-in.

Why are these features coming to React 19 instead of React 18.3? It seems that there will not be a 18.3 release because these features include [some minor breaking changes](https://twitter.com/rickhanlonii/status/1747338240099487877).

When is React 19 coming? There is no ETA yet, but all the features mentioned in this post are already working. I wouldn't recommend using them yet, though - using a canary release in production is not a good idea (even if Next.js does it).

It's really nice to see that the React core team is working on improving the developer experience of all React developers, not only those working on SSR apps. It also seems they're listening to the community feedback - data fetching and form handling are [very common pain points](https://marmelab.com/blog/2022/09/20/react-i-love-you.html).

I'm looking forward to seeing these features in a stable release of React!