In the ground since Mon Jan 27 2025
Last watered inMon Jan 27 2025
React use() Hook
React's use() hook is a powerful utility that unwraps Promises and Context values. It's essential for handling async data in client components, especially with Next.js 15+ where route parameters are now Promises.
What is use()?
The use() hook allows you to unwrap Promises and Context values synchronously in React components. It's React's solution for handling async data in client components where you can't use await.
Why It Exists
In React, you can't use await directly in component bodies (only in async functions). Client components can't be async functions, so use() provides a way to unwrap Promises synchronously while maintaining React's rendering model.
Basic Usage
Unwrapping Promises
1"use client"
2import { use } from "react";
3
4interface Props {
5 params: Promise<{ topic: string }>;
6}
7
8export default function TopicScreen({ params }: Props) {
9 const resolvedParams = use(params);
10 const topic = resolvedParams.topic;
11
12 return <div>{topic}</div>;
13}
Unwrapping Context
1"use client"
2import { use, createContext } from "react";
3
4const ThemeContext = createContext<Promise<string>>();
5
6export default function Component() {
7 const theme = use(ThemeContext);
8 return <div>Current theme: {theme}</div>;
9}
Before vs After (Next.js 15+ Migration)
Before (Next.js 14 and earlier)
1interface Props {
2 params: { topic: string }; // Synchronous object
3}
4
5export default function TopicScreen({ params }: Props) {
6 const topic = params.topic; // Direct access
7 return <div>{topic}</div>;
8}
After (Next.js 15+)
1"use client"
2import { use } from "react";
3
4interface Props {
5 params: Promise<{ topic: string }>; // Async Promise
6}
7
8export default function TopicScreen({ params }: Props) {
9 const resolvedParams = use(params); // Unwrap with use()
10 const topic = resolvedParams.topic;
11 return <div>{topic}</div>;
12}
When to Use use() vs await
| Scenario | Use await | Use use() |
|----------|-------------|-------------|
| Server Components | ✅ Yes | ❌ No |
| Client Components | ❌ No | ✅ Yes |
| Async Functions | ✅ Yes | ❌ No |
| Component Body | ❌ No | ✅ Yes |
| Event Handlers | ✅ Yes | ❌ No |
Key Benefits
- Works in Client Components: No need to make the component async
- Suspense Integration: React can suspend while the Promise resolves
- Type-Safe: TypeScript understands the unwrapped type
- Consistent API: Same pattern for Promises and Context
How It Works Under the Hood
When you call use(promise):
- React checks if the Promise is resolved
- If resolved: Returns the value immediately
- If pending: Throws the Promise (Suspense catches it)
- React re-renders when the Promise resolves
This is why Suspense boundaries are important when using use() with Promises.
Error Handling
Always wrap use() in error boundaries when dealing with Promises that might reject:
1"use client"
2import { use } from "react";
3import { ErrorBoundary } from "react-error-boundary";
4
5function TopicContent({ params }: { params: Promise<{ topic: string }> }) {
6 const resolved = use(params);
7 return <div>{resolved.topic}</div>;
8}
9
10export default function TopicScreen({ params }: Props) {
11 return (
12 <ErrorBoundary fallback={<div>Error loading topic</div>}>
13 <Suspense fallback={<div>Loading...</div>}>
14 <TopicContent params={params} />
15 </Suspense>
16 </ErrorBoundary>
17 );
18}
Real-World Example: Next.js Route Params
This is the exact pattern used when handling async route parameters in Next.js 15+:
1"use client";
2import { use } from "react";
3import { PageHeading } from "@/domains/garden-components/page-heading";
4
5interface Props {
6 params: Promise<{ topic: string }>;
7}
8
9export default function TopicScreen({ params }: Props) {
10 const resolvedParams = use(params);
11 const topicData = getTopicData(resolvedParams.topic);
12
13 return (
14 <PageHeading title={topicData.title}>
15 {/* Component content */}
16 </PageHeading>
17 );
18}
Related Concepts
- Suspense Boundaries: use() works seamlessly with Suspense for loading states
- Streaming: Enables progressive rendering as data becomes available
- Server Components: Server components use await, client components use use()
- React 19: The use() hook is part of React's modern async data handling
Production Considerations
- Error Handling: Always wrap use() in error boundaries
- Loading States: Use Suspense boundaries for pending Promises
- Type Safety: Ensure Promise types match expected values
- Performance: use() enables React to optimize rendering and streaming
Common Pitfalls
❌ Don't Use await in Client Components
1// ❌ Wrong - Client components can't be async
2"use client"
3export default async function Component({ params }) {
4 const resolved = await params; // This won't work!
5}
✅ Use use() Instead
1// ✅ Correct - Use use() hook
2"use client"
3import { use } from "react";
4
5export default function Component({ params }: { params: Promise<any> }) {
6 const resolved = use(params); // This works!
7}
❌ Don't Forget Suspense Boundaries
1// ❌ Wrong - Missing Suspense boundary
2export default function Component({ params }) {
3 const resolved = use(params); // Might cause issues
4}
✅ Wrap in Suspense
1// ✅ Correct - Proper Suspense handling
2export default function Component({ params }) {
3 return (
4 <Suspense fallback={<div>Loading...</div>}>
5 <Content params={params} />
6 </Suspense>
7 );
8}
9
10function Content({ params }) {
11 const resolved = use(params);
12 return <div>{resolved.data}</div>;
13}
Why Next.js 15+ Made Params Async
Next.js 15+ made route parameters async to enable:
- Better Streaming: Progressive rendering as params resolve
- Suspense Integration: Proper loading states during navigation
- Performance: Optimized rendering and data fetching
- Consistency: Unified async data handling across the framework
The use() hook is React's elegant solution for handling this in client components!