Client Data Fetching
Fetching data on the client side in Next.js
🌐 What is Client Data Fetching?
Client data fetching loads data in the browser after the page renders. It's perfect for dynamic content that updates frequently or requires user interaction before loading.
// Simple client-side fetch example
'use client'
export default function Posts() {
const [posts, setPosts] = useState([])
useEffect(() => {
fetch('/api/posts')
.then(res => res.json())
.then(data => setPosts(data))
}, [])
return <div>{posts.map(post => <p key={post.id}>{post.title}</p>)}</div>
}
Key Concepts
Dynamic Updates
Data loads after page renders
'use client'
const [data, setData] = useState(null)
Fast Initial Load
Page shows immediately, data loads after
if (!data) return <Loading />
User Interactions
Fetch data based on user actions
onClick={() => fetchData()}
Real-time Updates
Refresh data without page reload
setInterval(() => fetchData(), 5000)
🔹 Basic Client Fetch Example
Client-side data fetching happens in the browser after the component mounts. This approach is ideal for data that changes frequently or depends on user interaction.
'use client'
import { useState, useEffect } from 'react'
export default function UserProfile() {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch('https://api.example.com/user')
.then(response => response.json())
.then(data => {
setUser(data)
setLoading(false)
})
}, [])
if (loading) return <p>Loading...</p>
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
)
}
🔹 Using SWR for Data Fetching
SWR is a React hook library that makes client-side data fetching easier with built-in caching, revalidation, and error handling.
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then(res => res.json())
export default function Posts() {
const { data, error, isLoading } = useSWR('/api/posts', fetcher)
if (error) return <div>Failed to load</div>
if (isLoading) return <div>Loading...</div>
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
🔹 Fetch on Button Click
Sometimes you want to fetch data only when the user performs an action, like clicking a button.
'use client'
import { useState } from 'react'
export default function SearchUsers() {
const [users, setUsers] = useState([])
const [query, setQuery] = useState('')
const handleSearch = async () => {
const response = await fetch(`/api/search?q=${query}`)
const data = await response.json()
setUsers(data)
}
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search users..."
/>
<button onClick={handleSearch}>Search</button>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
)
}
🔹 Error Handling
Always handle errors gracefully when fetching data on the client side to improve user experience.
'use client'
import { useState, useEffect } from 'react'
export default function DataComponent() {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch('/api/data')
.then(res => {
if (!res.ok) throw new Error('Failed to fetch')
return res.json()
})
.then(data => setData(data))
.catch(err => setError(err.message))
.finally(() => setLoading(false))
}, [])
if (loading) return <p>Loading...</p>
if (error) return <p>Error: {error}</p>
return <div>{JSON.stringify(data)}</div>
}