The new use() useEffect patterns
A practical guide with before & after code examples, rules, and when to use use() vs. traditional hooks.
Intro — why use() matters
React 19 introduced use(), a compact API that lets components read the value of a Promise or a Context directly during render. That means less boilerplate around async state, fewer useEffect patterns for initial fetches, and smoother integration with Suspense and Server Components.
Quick summary — what use() does
- Read promises: Pass a Promise (or "resource") to use() — the component will suspend until it resolves.
- Read context in more places: use() can be used to read a Context value from Server Components.
- Works with Suspense & Error Boundaries: When the Promise is pending, React will show the nearest Suspense fallback.
Before & After — replace common useEffect patterns
1) Fetching data (initial render)
Before: the classic useEffect + useState pattern
// Before (React 18 style)
import { useEffect, useState } from "react";
export default function User() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/user")
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
return <div>{user.name}</div>;
}
After: using use() in a Server Component (clean, declarative)
// After (React 19, Server Component)
import { use } from "react";
export default function User() {
const user = use(fetch("/api/user").then(res => res.json()));
return <div>{user.name}</div>;
}
2) Loading UI with Suspense
Before: manual loading state handling
function Posts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function load() {
const res = await fetch("/api/posts");
const data = await res.json();
setPosts(data);
setLoading(false);
}
load();
}, []);
if (loading) return <Spinner />;
return <PostsList posts={posts} />;
}
After: let Suspense handle the pending state
import { Suspense, use } from "react";
function PostsContent() {
const posts = use(fetch("/api/posts").then(r => r.json()));
return <PostsList posts={posts} />;
}
export default function Posts() {
return (
<Suspense fallback=<Spinner />>
<PostsContent />
</Suspense>
);
}
3) Consuming Context in Server Components
Before: Server Components couldn't use useContext
// ❌ Not possible previously in pure Server Components:
// function Header() {
// const theme = useContext(ThemeContext); // Error in Server Components
// return <h1>Hello</h1>;
// }
After: use() can read Context in Server Components
import { use } from "react";
import { ThemeContext } from "./theme-context";
function Header() {
const theme = use(ThemeContext); // works in Server Components
return <h1 className={theme}>Hello</h1>;
}
4) Form actions & useActionState
React 19 also introduced helpers like useActionState that pair nicely with use() to get action results directly during render (useful in Server Component + Action workflows).
import { use, useActionState } from "react";
function sendMessage(prev, formData) {
return formData.get("msg");
}
export default function ContactForm() {
const [state, formAction] = useActionState(sendMessage, null);
const message = use(state); // unwrap the action result
return (
<>
<form action={formAction}>
<input name="msg" />
</form>
<p>{message}</p>
</>
);
}
Rules, gotchas, and best practices
- Server vs Client: When you pass a Promise to use(), that pattern is designed for Server Components — passing Promises to use() in Client Components is not the intended flow. You can use use() to read Context in both client & server, but promise-resolving during client render is not the common pattern.
- Top-level use: Like hooks, call use() only inside components or custom hooks (the same "don’t call hooks conditionally" guidance is simpler here: use() allowed in more places, but keep readability).
- Suspense & Error Boundaries: Wrap components that call use() with <Suspense fallback=...> to show fallbacks while Promises resolve, and use Error Boundaries to catch errors from rejected promises.
- Avoid mixing responsibilities: Prefer server-side data resolution via use() and keep client components for interactivity — this keeps initial rendering fast and code easier to reason about.
When should you keep using useEffect ?
Use useEffect for client-only side effects (DOM APIs, subscriptions, event listeners, timers). Use use() to simplify data fetching and context consumption that can or should run during server render.
Example: Converting an existing component
Here's a tiny conversion checklist you can copy into your repo:
- Identify code that fetches data only on mount (the typical useEffect initial load).
- Move that fetching into a Promise you pass to use() from a Server Component (or wrap in a data loader).
- Wrap the new server-rendered child in <Suspense> and provide a friendly fallback.
- Keep client-only concerns in client components and wire them in after hydration.
Conclusion
use() is a pragmatic addition that reduces boilerplate and works beautifully with Suspense and Server Components. It doesn't replace all hooks — but it does simplify the most common pattern developers have used useEffect for: initial data loading.







