React v19

React v19

React 19 is now stable!

Β·

6 min read

In this blog, we are covering the key points and major features that React 19 brings with it.

useTransition

In React 19, support has been added for using async functions in transitions, enabling automatic handling of pending states, errors, forms, and optimistic updates.

// Using pending state from Actions
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;
      } 
      redirect("/path");
    })
  };

  return (
    <div>
      <input value={name} onChange={(event) => setName(event.target.value)} />
      <button onClick={handleSubmit} disabled={isPending}>
        Update
      </button>
      {error && <p>{error}</p>}
    </div>
  );
}

When you start an async transition, it's like hitting the "loading" button on your favorite game. The "isPending" light turns on, and while the game fetches new levels, you can still play around without freezing. Once it's done, the "isPending" light goes off, and you're ready to rock with the new data. It's like magic, but with less wizard hats! πŸ§™β€β™‚οΈβœ¨

To make the common cases easier for Actions, we’ve added a new hook called useActionState:

const [error, submitAction, isPending] = useActionState(
  async (previousState, newName) => {
    const error = await updateName(newName);
    if (error) {
      // You can return any result of the action.
      // Here, we return only the error.
      return error;
    }

    // handle success
    return null;
  },
  null,
);

React DOM: New hook: useFormStatus

In design systems, components often need to know about the form they're part of, but passing this information through props can be complicated. To simplify this, React 19 introduced a new tool called useFormStatus. It helps components easily access form details without the hassle of passing props around.

import {useFormStatus} from 'react-dom';

function DesignButton() {
  const {pending} = useFormStatus();
  return <button type="submit" disabled={pending} />
}

New hook: useOptimistic

Another common UI pattern when performing a data mutation is to show the final state optimistically while the async request is underway. In React 19, we’re adding a new hook called useOptimistic to make this easier.

Imagine you're ordering a pizza online. With the useOptimistic hook, it's like seeing the pizza tracker say "Out for delivery" the moment you click order, even if the chef is still tossing the dough. It's a tasty illusion that keeps you excited while you wait! πŸ•πŸ˜„

function ChangeName({currentName, onUpdateName}) {
  const [optimisticName, setOptimisticName] = useOptimistic(currentName);

  const submitAction = async formData => {
    const newName = formData.get("name");
    setOptimisticName(newName);
    const updatedName = await updateName(newName);
    onUpdateName(updatedName);
  };

  return (
    <form action={submitAction}>
      <p>Your name is: {optimisticName}</p>
      <p>
        <label>Change Name:</label>
        <input
          type="text"
          name="name"
          disabled={currentName !== optimisticName}
        />
      </p>
    </form>
  );
}

The useOptimistic hook will immediately render the optimisticName while the updateName request is in progress. When the update finishes or errors, React will automatically switch back to the currentName value.

For more information, see the docs for useOptimistic.

New API: use

In React 19 we’re introducing a new API to read resources in render: use.

For example, you can read a promise with use, and React will Suspend until the promise resolves:

import {use} from 'react';

function Comments({commentsPromise}) {
  // `use` will suspend until the promise resolves.
  const comments = use(commentsPromise);
  return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
  // When `use` suspends in Comments,
  // this Suspense boundary will be shown.
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments commentsPromise={commentsPromise} />
    </Suspense>
  )
}

New React DOM Static APIs

React19 introduced two new APIs to react-dom/static for static site generation:

These new APIs improve on renderToString by waiting for data to load for static HTML generation. They are designed to work with streaming environments like Node.js Streams and Web Streams. For example, in a Web Stream environment, you can prerender a React tree to static HTML with prerender :

import { prerender } from 'react-dom/static';

async function handler(request) {
  const {prelude} = await prerender(<App />, {
    bootstrapScripts: ['/main.js']
  });
  return new Response(prelude, {
    headers: { 'content-type': 'text/html' },
  });
}

Prerender APIs wait for all the data to load before giving back the static HTML stream. Streams can be turned into strings or sent as a streaming response. They don't support streaming content as it loads, which is something the current React DOM server rendering APIs can do.

For more information, see React DOM Static APIs.

<Context> as a provider

In React 19, you can render <Context> as a provider instead of <Context.Provider>:


const ThemeContext = createContext('');

function App({children}) {
  return (
    <ThemeContext value="dark">
      {children}
    </ThemeContext>
  );  
}

New Context providers can use <Context> and we will be publishing a codemod to convert existing providers. In future versions we will deprecate <Context.Provider>.

useDeferredValue initial value

React19 added an initialValue option to useDeferredValue:


function Search({deferredValue}) {
  // On initial render the value is ''.
  // Then a re-render is scheduled with the deferredValue.
  const value = useDeferredValue(deferredValue, '');

  return (
    <Results query={value} />
  );
}

When initialValue is provided, useDeferredValue will return it as value for the initial render of the component, and schedules a re-render in the background with the deferredValue returned.

For more, see useDeferredValue.

Support for stylesheets

React 19 makes handling styles much easier! Now, you can tell React the priority of your stylesheets, and it will make sure they load in the right order. This means your app looks good without worrying about styles clashing or loading too late. 😊


function ComponentOne() {
  return (
    <Suspense fallback="loading...">
      <link rel="stylesheet" href="foo" precedence="default" />
      <link rel="stylesheet" href="bar" precedence="high" />
      <article class="foo-class bar-class">
        {...}
      </article>
    </Suspense>
  )
}

function ComponentTwo() {
  return (
    <div>
      <p>{...}</p>
      <link rel="stylesheet" href="baz" precedence="default" />  <-- will be inserted between foo & bar
    </div>
  )
}

Support for preloading resources

React 19 helps your website load faster by telling the browser what it needs early on. It has new tools to make sure everything loads smoothly, so your app feels super quick and awesome! πŸš€


<!-- the above would result in the following DOM/HTML -->
<html>
  <head>
    <!-- links/scripts are prioritized by their utility to early loading, not call order -->
    <link rel="prefetch-dns" href="https://...">
    <link rel="preconnect" href="https://...">
    <link rel="preload" as="font" href="https://.../path/to/font.woff">
    <link rel="preload" as="style" href="https://.../path/to/stylesheet.css">
    <script async="" src="https://.../path/to/some/script.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

Connect with Me on Social Media πŸ“±

I love engaging with fellow developers and readers! If you have any questions, feedback, or just want to connect, feel free to reach out to me on social media. You can find me on Twitter and LinkedIn. I look forward to hearing from you and continuing the conversation! 🌐

Β