React 19 Beta Release: A Quick Guide

Exploring the New Features and Enhancements of React 19 Beta

Zachary Lee
JavaScript in Plain English

--

Photo by Lautaro Andreani on Unsplash

Originally published in my newsletter.

React 19 Beta has been announced, it introduces several new concepts that simplify state management, error handling, and asynchronous operations in React applications. In this article, I’ll quickly summarize these features so you can quickly grasp the main points.

Simplifying Asynchronous Operations with Actions

One of the standout features in React 19 is the introduction of “Actions”. Actions are a new way to handle data mutations and state updates following asynchronous operations. Previously, developers had to manage pending states, errors, and optimistic updates manually, which could get cumbersome. React 19 aims to simplify this process significantly.

Example: Updating a User’s Name

Consider a common use case where a user submits a form to update their name. In earlier versions of React, you might handle the asynchronous API request like this:

function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);

const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
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>
);
}

With React 19, you can utilize the useTransition hook to handle the pending state more succinctly:

function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();

const handleSubmit = async () => {
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>
);
}

This new approach automatically manages the pending state and error handling, making the code cleaner and more manageable.

Enhancements in Form Handling

React 19 introduces several improvements in how forms can be managed, particularly through the integration of Actions into form elements. This new feature simplifies form submission processes and error handling.

Simplified Form Submission

Using Actions, forms can now automatically handle data submissions without the need for cumbersome error and state management. Here’s how you might use it:

function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get("name"));
if (error) {
return error;
}
redirect("/path");
}
);

return (
<form action={submitAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}

This code snippet shows a form that utilizes the useActionState hook to manage the submission process. This hook encapsulates the logic for handling pending states, errors, and the actual data submission, reducing boilerplate and improving readability.

New Hook — useFormStatus

This hook allows components to access the form’s status directly, treating it much like context. This is particularly useful for designing components that need to react to the form’s state.

import { useFormStatus } from 'react-dom';

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

New Hook — useOptimistic

This hook allows developers to optimistically update the UI, providing instant feedback before the operation completes.

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>
<label>Change Name:</label>
<input type="text" name="name" disabled={currentName !== optimisticName} />
</form>
);
}

The useOptimistic hook will render optimisticName immediately while the updateName request is in progress. When the update completes or an error occurs, React automatically switches back to the currentName value.

New API: use

use in React 19 designed to read resources during render. It supports suspending components until resources, like promises, resolve, enhancing the integration with React's Suspense feature.

import { use } from 'react';

function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({ commentsPromise }) {
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}

React Server Components

React Server Components allow rendering of components ahead of time, in a distinct environment separate from the client application. This feature enables execution at build time on a CI server or per request on a web server, improving performance and resource efficiency.

While Server Components are stable across major releases, the APIs for bundling or frameworks might change in minor releases. To ensure compatibility, it’s recommended to pin a specific React version or use the Canary release.

Server Actions

Server Actions in React 19 enable client components to invoke asynchronous server-executed functions. This interaction is streamlined through a "use server" directive, automating the reference passing between client and server.

Enhancements in React 19

ref as a Prop

React 19 allows ref to be passed directly as a prop to function components, simplifying the component's code by eliminating the need for forwardRef.

function MyInput({ placeholder, ref }) {
return <input placeholder={placeholder} ref={ref} />;
}

<MyInput ref={React.createRef()} />

Improved Hydration Error Handling

React 19 improves how hydration errors are reported, offering a clearer and more detailed message when mismatches occur.

<Context> as a Provider

The new React version allows <Context> to be used directly as a provider:

const ThemeContext = createContext('');

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

Cleanup Functions for ref

React 19 supports returning a cleanup function from ref callbacks, aiding in better resource management when components unmount.

<input ref={ref => {
// Actions on ref creation
return () => {
// Cleanup actions
};
}} />

useDeferredValue with Initial Value

React 19 introduces an initialValue for useDeferredValue, allowing immediate use of a default value before any deferred updates.

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} />;
}

Advanced Document Metadata Management

React 19 supports rendering <title>, <meta>, and <link> tags within component renders, automatically managing their placement in the document's <head>.

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 mc-squared...</p>
</article>
);
}

When React renders this component, it will see the <title> <link> and <meta> tags, and automatically hoist them to the <head> section of document.

Improved Handling of Stylesheets and Scripts

React 19 enhances how stylesheets and scripts are handled, ensuring proper loading order and avoiding duplication.

Stylesheet Management Example:

function ComponentOne() {
return (
<Suspense fallback="loading...">
<link rel="stylesheet" href="foo" precedence="default" />
<link rel="stylesheet" href="bar" precedence="high" />
<article className="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>
)
}


function App() {
return <>
<ComponentOne />
...
<ComponentOne /> // won't lead to a duplicate stylesheet link in the DOM
</>
}

Async Scripts Example:

function MyComponent() {
return (
<div>
<script async={true} src="..." />
Hello World
</div>
)
}

function App() {
<html>
<body>
<MyComponent>
...
<MyComponent> // won't lead to duplicate script in the DOM
</body>
</html>
}

Support for Preloading Resources

React 19 introduces new APIs for loading and preloading resources, enhancing performance and user experience during initial loads and subsequent updates.

import { prefetchDNS, preconnect, preload, preinit } from 'react-dom';

function MyComponent() {
// Eagerly loads and executes the script
preinit('https://.../path/to/some/script.js', { as: 'script' });

// Preloads the font
preload('https://.../path/to/font.woff', { as: 'font' });

// Preloads the stylesheet
preload('https://.../path/to/stylesheet.css', { as: 'style' });

// DNS prefetch for upcoming requests
prefetchDNS('https://...');

// Preconnects to the server for upcoming requests
preconnect('https://...');
}

// Resulting HTML from server-side rendering
<html>
<head>
<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>

These APIs help optimize resource discovery and loading, improving performance by reducing load times during critical rendering paths.

Compatibility with Third-Party Scripts and Extensions

React 19 improves handling of third-party scripts and browser extensions during hydration. This enhancement prevents common mismatches and errors that occur when third-party scripts modify the DOM outside of React’s control.

Enhanced Hydration Compatibility

When hydrating, if React detects discrepancies due to third-party scripts, it now intelligently skips over unexpected tags rather than triggering re-render errors. This approach minimizes potential disruptions caused by external scripts and extensions, ensuring a smoother user experience.

Improved Error Reporting

React 19 streamlines error reporting by consolidating information and reducing the occurrence of duplicate error messages. This update provides clearer insights into component failures, especially within complex applications.

Additionally, React 19 introduces root options like onCaughtError, onUncaughtError, and onRecoverableError, offering more granular control over error handling across the application.

Full Support for Custom Elements

React 19 provides comprehensive support for custom elements, ensuring full compatibility with the Custom Elements Everywhere standards. This support facilitates the integration of custom elements within React applications, improving interoperability and component reusability.

Upgrade Guidance

This article is just a quick guide from the official blog, If you want to upgrade to React 19 beta now, you can check out this link. Hope you find it useful!

If you find this helpful, consider subscribing to my newsletter for more articles on web development. Feel free to share my posts!

In Plain English 🚀

Thank you for being a part of the In Plain English community! Before you go:

--

--