Client-Side Data Fetching in Next.js

Last Updated : 10 Jul, 2026

Client-Side Data Fetching in Next.js retrieves data in the browser after the page has loaded. It is commonly used for interactive, user-specific, or frequently changing data where the content does not need to be pre-rendered on the server.

client-side-rendering
  • Fetches data after the page loads in the browser.
  • Uses Client Components with React hooks like useEffect or libraries such as SWR.
  • Ideal for user-specific or frequently changing data.
  • Supports asynchronous updates without refreshing the page.

Client-side Data fetching with useEffect and Fetch

The simplest way to fetch data on the client side is using React's useEffect hook inside a Client Component. In the App Router, the "use client" directive enables React hooks such as useState and useEffect.

Create a Client Component and add the following code:

JavaScript
"use client";
import { useState, useEffect } from "react";
export default function ClientFetchExample() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((res) => res.json())
      .then((data) => {
        setPosts(data);
        setLoading(false);
      });
  }, []);
  if (loading) return <p>Loading...</p>;
  return (
    <div>
      <h1>Client-Side Fetched Posts</h1>
      <ul>
        {posts.slice(0, 5).map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}
  • Data is fetched after the page loads.
  • The loading state shows feedback until data arrives.

Client-side Data fetching with SWR

SWR is a popular data-fetching library created by the Next.js team. It provides built-in caching, automatic revalidation, request deduplication, and a simple API for client-side data fetching.

  • Built-in caching for faster repeated requests
  • Automatic revalidation to keep data fresh
  • Real-time data updates without extra setup
  • Simple and developer-friendly API

Install the SWR package using the following command:

npm install swr
JavaScript
"use client";
import useSWR from "swr";
const fetcher = (url) => fetch(url).then((res) => res.json());
export default function SWRExample() {
  const { data, error, isLoading } = useSWR(
    "https://jsonplaceholder.typicode.com/posts",
    fetcher
  );
  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Failed to load data</p>;
  return (
    <div>
      <h1>Posts (using SWR)</h1>
      <ul>
        {data.slice(0, 5).map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}
  • The fetcher function fetches data from the given API and returns it as JSON.
  • The useSWR hook calls this fetcher, manages loading/error states, and provides the API response.
  • The component shows a loading message first, then displays the first 5 post titles once the data is ready.

Features of Client-Side Data Fetching

  • Runs entirely in the browser.
  • Updates the UI asynchronously after data is fetched.
  • Supports loading and error states.
  • Suitable for real-time or frequently changing data.
  • Works with REST APIs, GraphQL, and external services.
  • Integrates with libraries like SWR and React Query.

Note: Use client-side data fetching for interactive, user-specific, or frequently changing content. For SEO-critical or static content, prefer Server Components or server-side data fetching.

Implementation of Client-Side Data Fetching

Follow the steps below to fetch data from an external API in a Client Component.

Step 1: Create a Next.js Project

npx create-next-app@latest client-fetch-app

Step 2: Navigate to the Project Directory

cd client-fetch-app

Step 3: Project Structure

client-fetch-app/

├── app/
│ ├── posts/
│ │ └── page.js
│ └── page.js

├── package.json
└── ...

Step 4: Fetch Data in a Client Component

Create the following file:

app/posts/page.js

JavaScript
"use client";

import { useEffect, useState } from "react";

export default function Posts() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((res) => res.json())
      .then((data) => {
        setPosts(data.slice(0, 10));
        setLoading(false);
      });
  }, []);

  if (loading) {
    return (
      <main style={{ padding: "40px", fontFamily: "Arial" }}>
        <h2>Loading posts...</h2>
      </main>
    );
  }

  return (
    <main
      style={{
        minHeight: "100vh",
        background: "#f6f8fc",
        padding: "40px 20px",
        fontFamily: "Arial, sans-serif",
      }}
    >
      <div
        style={{
          maxWidth: "900px",
          margin: "0 auto",
        }}
      >
        <h1
          style={{
            textAlign: "center",
            fontSize: "42px",
            marginBottom: "10px",
          }}
        >
          Client-Side Fetched Posts
        </h1>

        <p
          style={{
            textAlign: "center",
            color: "#666",
            marginBottom: "40px",
          }}
        >
          Posts loaded after the page is rendered in the browser.
        </p>

        {posts.map((post, index) => (
          <div
            key={post.id}
            style={{
              display: "flex",
              justifyContent: "space-between",
              alignItems: "center",
              background: "#fff",
              padding: "18px 24px",
              marginBottom: "16px",
              borderRadius: "12px",
              boxShadow: "0 4px 12px rgba(0,0,0,0.08)",
            }}
          >
            <div
              style={{
                display: "flex",
                alignItems: "center",
                gap: "20px",
              }}
            >
              <div
                style={{
                  width: "45px",
                  height: "45px",
                  borderRadius: "10px",
                  background: "#ede9fe",
                  color: "#6d28d9",
                  display: "flex",
                  justifyContent: "center",
                  alignItems: "center",
                  fontWeight: "bold",
                }}
              >
                {index + 1}
              </div>

              <span
                style={{
                  fontSize: "18px",
                  fontWeight: "600",
                }}
              >
                {post.title}
              </span>
            </div>

            <span
              style={{
                background: "#ede9fe",
                color: "#6d28d9",
                padding: "8px 14px",
                borderRadius: "20px",
                fontWeight: "bold",
              }}
            >
              Post #{index + 1}
            </span>
          </div>
        ))}
      </div>
    </main>
  );
}

Step 5: Run the Application

Start the development server:

npm run dev

Open the following URL in your browser:

http://localhost:3000/posts

Output:

Screenshot-2026-07-09-150605
Comment

Explore