How to Fetch Data in Client Component in Next.js
In Next.js client components, fetch data by using React's
useEffect hook combined with the fetch API inside the component. This approach runs the data fetching on the browser after the component mounts, enabling dynamic client-side data loading.Syntax
Use React's useEffect hook to run data fetching code after the component mounts. Store the fetched data in a state variable using useState. The fetch API is used to request data from an endpoint.
useState: Holds the data and loading state.useEffect: Runs the fetch call once after the component loads.fetch: Makes the HTTP request to get data.
jsx
import React, { useState, useEffect } from 'react'; export default function ClientComponent() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch('/api/data') .then((res) => res.json()) .then((json) => { setData(json); setLoading(false); }); }, []); if (loading) return <p>Loading...</p>; return <pre>{JSON.stringify(data, null, 2)}</pre>; }
Output
<p>Loading...</p> (initially), then JSON data displayed as formatted text
Example
This example shows a client component fetching user data from a public API after it loads. It displays a loading message while waiting and then shows the user name and email.
jsx
import React, { useState, useEffect } from 'react'; export default function UserProfile() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchUser() { const response = await fetch('https://jsonplaceholder.typicode.com/users/1'); const data = await response.json(); setUser(data); setLoading(false); } fetchUser(); }, []); if (loading) return <p>Loading user data...</p>; return ( <div> <h2>{user.name}</h2> <p>Email: {user.email}</p> </div> ); }
Output
<h2>Leanne Graham</h2><p>Email: Sincere@april.biz</p>
Common Pitfalls
Common mistakes when fetching data in client components include:
- Not using
useEffect, causing fetch to run on every render and creating infinite loops. - Forgetting to handle loading or error states, leading to poor user experience.
- Trying to fetch data directly in the component body, which runs on server and client and breaks client-only logic.
Always wrap fetch calls inside useEffect with an empty dependency array to run once after mount.
jsx
/* Wrong: fetch runs on every render causing infinite loop */ import React, { useState } from 'react'; export default function WrongFetch() { const [data, setData] = useState(null); fetch('/api/data') .then(res => res.json()) .then(json => setData(json)); return <pre>{JSON.stringify(data)}</pre>; } /* Right: fetch inside useEffect runs once after mount */ import React, { useState, useEffect } from 'react'; export default function RightFetch() { const [data, setData] = useState(null); useEffect(() => { fetch('/api/data') .then(res => res.json()) .then(json => setData(json)); }, []); return <pre>{JSON.stringify(data)}</pre>; }
Quick Reference
Tips for fetching data in Next.js client components:
- Use
useEffectwith an empty dependency array to fetch data once on mount. - Use
useStateto store fetched data and loading states. - Handle loading and error states gracefully for better UX.
- Use async/await inside
useEffector promise chaining withthen. - Remember client components run in the browser, so fetch calls happen client-side.
Key Takeaways
Use React's useEffect hook to fetch data after the client component mounts.
Store fetched data in state with useState to trigger re-render with new data.
Always include an empty dependency array in useEffect to avoid infinite loops.
Handle loading states to inform users while data is being fetched.
Client components fetch data on the browser, enabling dynamic content updates.