If you’re already comfortable with React and looking to level up, React 19 brings some really smart new features and improvements. In this post we’ll cover six of the most useful ones — each with a “before” (React 18 or earlier) and an “after” (React 19) snippet so you can see how things have changed.
1) Pass ref as a Prop (No More forwardRef)
Previously, forwarding refs in functional components required React.forwardRef and a little boilerplate. React 19 lets you pass ref directly as a normal prop in many cases.
Before (React 18)
import React, { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Parent() {
const inputRef = React.useRef();
return <MyInput ref={inputRef} p...
If you’re already comfortable with React and looking to level up, React 19 brings some really smart new features and improvements. In this post we’ll cover six of the most useful ones — each with a “before” (React 18 or earlier) and an “after” (React 19) snippet so you can see how things have changed.
1) Pass ref as a Prop (No More forwardRef)
Previously, forwarding refs in functional components required React.forwardRef and a little boilerplate. React 19 lets you pass ref directly as a normal prop in many cases.
Before (React 18)
import React, { forwardRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Parent() {
const inputRef = React.useRef();
return <MyInput ref={inputRef} placeholder="Type here…" />;
}
After (React 19)
function MyInput({ placeholder, ref }) {
return <input placeholder={placeholder} ref={ref} />;
}
export default function Parent() {
const inputRef = React.useRef();
return <MyInput ref={inputRef} placeholder="Type here…" />;
}
2. New use() API for Resources/Promises
Why it matters: The new use() hook lets you synchronously “read” a promise or resource inside a render. It works nicely with Suspense and server-rendering.
Before (React 18)
function Comments({ promise }) {
const [comments, setComments] = React.useState(null);
React.useEffect(() => {
promise.then(data => setComments(data));
}, [promise]);
if (comments === null) {
return <div>Loading…</div>;
}
return comments.map(c => <p key={c.id}>{c.text}</p>);
}
After (React 19)
import { use } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map(c => <p key={c.id}>{c.text}</p>);
}
3. Built-in Form Actions + Hooks (useActionState, useFormStatus, useOptimistic)
React 19 introduces better support for form submissions and optimistic UI updates via Actions and hooks that track pending state.
Before (React 18)
function TodoList() {
const [items, setItems] = React.useState([]);
const [loading, setLoading] = React.useState(false);
const handleSubmit = async event => {
event.preventDefault();
setLoading(true);
const form = new FormData(event.target);
const res = await fetch('/api/add', { method: 'POST', body: form });
const newItem = await res.json();
setItems([...items, newItem]);
setLoading(false);
};
return (
<form onSubmit={handleSubmit}>
<input name="item" placeholder="Add todo…" />
<button type="submit" disabled={loading}>{loading ? "Saving…" : "Add"}</button>
</form>
);
}
After (React 19)
"use client";
import { useActionState } from 'react';
async function create(data) {
// server-side logic
return await fetch('/api/add', { method: 'POST', body: data }).then(r => r.json());
}
export default function TodoList() {
const [error, submit, isPending] = useActionState(create);
return (
<form action={submit}>
<input name="item" placeholder="Add todo…" />
<button type="submit" disabled={isPending}>
{isPending ? "Saving…" : "Add"}
</button>
{error && <p>{error}</p>}
</form>
);
}
4. Native Metadata, Stylesheets & Script Support in Components
React 19 allows you to declare metadata stylesheet dependencies and async scripts inside components more declaratively.
Before (react 18)
import React from 'react';
import { Helmet } from 'react-helmet';
function Page() {
return (
<>
<Helmet>
<title>My Page</title>
<meta name="description" content="Hello world"/>
<link rel="stylesheet" href="/styles.css"/>
</Helmet>
<div>Content</div>
</>
);
}
After (React 19)
function Page() {
return (
<>
<title>My Page</title>
<meta name="description" content="Hello world" />
<link rel="stylesheet" href="/styles.css" />
<div>Content</div>
</>
);
}
5. Improved Custom Element / Web Component Support
Now you can integrate Web Components more smoothly: props turn into properties (not just attributes) and SSR + client behave more consistently.
Before (React 18)
// Using a Web Component inside React – might need workarounds
<MyWebComponent some-prop="value" />
After (React 19)
<MyWebComponent someProp={value} />
Drop a comment below I’d love to hear your thoughts!