React 19 brings a plethora of exciting updates and functionalities, revolutionizing the way developers build modern web applications. In this article, we'll dive deep into the key features introduced in React 19, covering everything from Actions and Context Providers to improved error handling and support for async scripts.
Actions: Simplifying State Management
One of the most notable additions in React 19 is the introduction of Actions, which significantly simplifies state management and data mutation. Traditionally, handling pending states, errors, and sequential requests required manual intervention, leading to complex and error-prone code. However, with Actions, developers can streamline these processes, resulting in cleaner and more maintainable code.
For instance, let's consider a scenario where a user submits a form to update their name. In previous versions of React, managing pending states and errors required explicit handling using useState. However, with React 19's Actions, this process becomes much more straightforward and automated.
// Before Actions
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 the introduction of Actions, the same functionality can be achieved in a more concise and efficient manner:
// Using Actions for state management
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>
);
}
The useTransition hook provided by React 19 simplifies the management of pending states and error handling, making the code more concise and readable.
Context Providers and Ref as a Prop
In addition to Actions, React 19 introduces improvements in Context Providers and supports accessing ref
as a prop directly in function components. This simplifies the management of context providers and enhances the flexibility of function components.
// Rendering Context as a provider
const ThemeContext = createContext('');
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}
// Ref as a prop in function components
function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}
// Usage
<MyInput ref={ref} />
These enhancements not only improve developer productivity but also enhance the overall developer experience when working with React components.
Improved Error Handling
In React 19, error reporting and handling have seen significant improvements. Previously, errors in rendering caught by an Error Boundary would result in duplicate error throws and console logs. React 19 addresses this by logging a single error with comprehensive information, reducing clutter in error logs.
Moreover, React 19 introduces new root options for handling different types
of errors, such as onCaughtError
for errors caught by an Error Boundary,
onUncaughtError
for uncaught errors, and onRecoverableError
for
automatically recovered errors. These enhancements streamline error management and provide clearer insights into application errors.
These enhancements make it easier for developers to build robust and reliable applications using React.
Support for Stylesheets, Async Scripts, and Preloading Resources
React 19 also introduces support for stylesheets, async scripts, and preloading resources, improving performance and optimizing resource loading in React applications.
Example for Stylesheets
// Support for stylesheets
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>
)
}
Example for Async Scripts
// async scripts
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>
}
Example for Preloading resources
// Preloading resources
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/script.js', {as: 'script' }) // loads and executes this script eagerly
preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font
preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet
prefetchDNS('https://...') // when you may not actually request anything from this host
preconnect('https://...') // when you will request something but aren't sure what
}
<!-- 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>
These features enable developers to optimize their applications for better performance and user experience.
Compatibility with Third-Party Scripts and Extensions
In React 19, enhanced hydration to handle third-party scripts and browser extensions more effectively. Now, if an element rendered on the client doesn't match the server's HTML, React initiates a client re-render to correct it. Previously, such mismatches could lead to errors and additional renders.
What's new is that React 19 intelligently skips over unexpected tags in the
and , preventing mismatch errors caused by these elements. Even when a full document re-render is necessary due to an unrelated hydration issue, React preserves stylesheets inserted by third-party scripts and extensions, ensuring smoother rendering and error handling.This enhancement enhances the interoperability of React applications with external tools and libraries.
Support for Custom Elements
In React 19, custom elements receive comprehensive support, successfully passing all tests on Custom Elements Everywhere.
Previously, using Custom Elements in React posed challenges as React treated unknown props as attributes, not properties. React 19 introduces property support, operating smoothly on both client and server sides.
During Server Side Rendering, props of primitive types like string, number, or boolean true render as attributes. Non-primitive props like objects, symbols, functions, or false values are omitted. On the client side, props matching Custom Element properties are assigned as properties; otherwise, they become attributes. This enhancement streamlines custom element usage in React applications.
How to upgrade
See the React 19 Upgrade Guide for step-by-step instructions and a full list of breaking and notable changes.
Conclusion
In conclusion, React 19 introduces a wide range of enhancements and features that empower developers to build modern and efficient web applications. From streamlined state management with Actions to improved error handling and support for async scripts, React 19 elevates the development experience and sets new standards for building robust and reliable applications. Incorporating these features into your projects can lead to more scalable, maintainable, and performant React applications.