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>
}

🧠 Test Your Knowledge

When does client-side data fetching happen?