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 ;
});
export default function Parent() {
const inputRef = React.useRef();
return ;
}
After (React 19)
function MyInput({ placeholder, ref }) {
return ;
}
export default function Parent() {
const inputRef = React.useRef();
return ;
}
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 Loading…
;
}
return comments.map(c => {c.text}
);
}
After (React 19)
import { use } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map(c => {c.text}
);
}
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 (
{loading ? "Saving…" : "Add"}
);
}
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 (
{isPending ? "Saving…" : "Add"}
{error && {error}
}
);
}
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 (
My Page
Content
>
);
}
After (React 19)
function Page() {
return (
My Page
Content
>
);
}
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
After (React 19)
Drop a comment below I'd love to hear your thoughts!
More...
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 ;
});
export default function Parent() {
const inputRef = React.useRef();
return ;
}
After (React 19)
function MyInput({ placeholder, ref }) {
return ;
}
export default function Parent() {
const inputRef = React.useRef();
return ;
}
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 Loading…
;
}
return comments.map(c => {c.text}
);
}
After (React 19)
import { use } from 'react';
function Comments({ commentsPromise }) {
const comments = use(commentsPromise);
return comments.map(c => {c.text}
);
}
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 (
{loading ? "Saving…" : "Add"}
);
}
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 (
{isPending ? "Saving…" : "Add"}
{error && {error}
}
);
}
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 (
My Page
Content
>
);
}
After (React 19)
function Page() {
return (
My Page
Content
>
);
}
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
After (React 19)
Drop a comment below I'd love to hear your thoughts!
More...