How to Fetch Data in Nextjs

Introduction Next.js has become the de facto framework for building modern React applications, thanks to its seamless server-side rendering, static site generation, and robust data fetching capabilities. As developers increasingly rely on dynamic content from APIs, databases, and third-party services, choosing the right data fetching method is no longer optional—it’s critical. The difference betwe

Oct 25, 2025 - 13:30
Oct 25, 2025 - 13:30
 0

Introduction

Next.js has become the de facto framework for building modern React applications, thanks to its seamless server-side rendering, static site generation, and robust data fetching capabilities. As developers increasingly rely on dynamic content from APIs, databases, and third-party services, choosing the right data fetching method is no longer optionalits critical. The difference between a fast, SEO-friendly page and a sluggish, unreliable one often comes down to how and when data is fetched.

But with so many optionsfetch, getServerSideProps, getStaticProps, SWR, React Query, and morehow do you know which ones you can truly trust? Not all methods are created equal. Some are optimized for performance, others for developer experience, and some for scalability under heavy traffic. In this guide, well cut through the noise and present the top 10 proven, production-tested ways to fetch data in Next.js that you can rely on, backed by real-world use cases, performance benchmarks, and official documentation.

Whether youre building a content-heavy blog, an e-commerce storefront, or a real-time dashboard, this guide will help you select the right strategy for your needsand avoid common pitfalls that lead to hydration errors, slow loads, or broken SEO.

Why Trust Matters

In the world of web development, trust isnt a buzzwordits a requirement. When you fetch data in a Next.js application, youre not just retrieving JSON responses. Youre shaping user experience, influencing search engine rankings, and determining whether your app feels responsive or broken.

Untrusted data fetching methods can lead to:

  • Flash of unstyled content (FOUC) or hydration mismatches
  • Slow Core Web Vitals scores, hurting SEO
  • Increased bounce rates due to delayed content
  • Broken server-side rendering, making pages invisible to crawlers
  • Unnecessary client-side re-renders that drain battery and bandwidth

Trusted methods, on the other hand, are those that:

  • Follow Next.js best practices as defined by the Vercel team
  • Integrate seamlessly with Reacts concurrent rendering model
  • Minimize network requests and maximize caching
  • Support incremental static regeneration (ISR) where applicable
  • Have active community support and regular updates

For example, using fetch() directly on the client without any fallbacks may seem simple, but it can break SSR entirely. Meanwhile, getServerSideProps ensures every request gets fresh databut at the cost of server load if overused. Trust means understanding trade-offs and selecting the right tool for the right job.

This section isnt about popularity. Its about reliability. The methods listed below have been battle-tested across thousands of production applications, reviewed in official Next.js documentation, and endorsed by the React and Vercel communities. Theyre not just trendytheyre proven.

Top 10 How to Fetch Data in Next.js

1. getServerSideProps (SSR)

getServerSideProps is one of the most trusted data fetching methods in Next.js. It runs on the server for every incoming request, allowing you to fetch fresh data before the page is rendered. This makes it ideal for pages that require user-specific or highly dynamic contentsuch as dashboards, personalized feeds, or authenticated routes.

Example:

export async function getServerSideProps(context) {

const res = await fetch('https://api.example.com/posts');

const posts = await res.json();

return {

props: { posts },

};

}

export default function Posts({ posts }) {

return (

<ul>

{posts.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Guarantees fresh data on every request
  • Perfect for SEO-critical pages with dynamic content
  • Rendered entirely on the serverno client-side hydration issues
  • Officially supported and documented by Vercel

Use when: You need real-time data, user authentication, or personalized content. Avoid for static content like blogs or product listings that dont change often.

2. getStaticProps (SSG) with revalidate

getStaticProps is Next.jss powerhouse for static site generation. When combined with the revalidate option, it enables Incremental Static Regeneration (ISR)a game-changer for performance and scalability.

Example:

export async function getStaticProps({ revalidate = 60 }) {

const res = await fetch('https://api.example.com/posts');

const posts = await res.json();

return {

props: { posts },

revalidate: 60, // Regenerate every 60 seconds

};

}

export default function Posts({ posts }) {

return (

<ul>

{posts.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Delivers lightning-fast page loads (pre-rendered at build time)
  • Reduces server load significantly
  • Supports SEO with static HTML
  • revalidate ensures content stays fresh without full rebuilds
  • Used by major sites like Netflix, Airbnb, and The New York Times

Use when: Content updates infrequently but needs to stay reasonably currentproduct listings, blog posts, news articles. Ideal for high-traffic sites that need to scale without costly server resources.

3. fetch() with React Server Components (RSC)

With the introduction of React Server Components in Next.js 13+ and the App Router, fetch() has become the default and recommended way to fetch data on the server. Unlike getServerSideProps, fetch() works directly inside components without needing a special function.

Example:

// app/posts/page.js

export default async function Posts() {

const res = await fetch('https://api.example.com/posts', {

next: { revalidate: 60 },

});

const posts = await res.json();

return (

<ul>

{posts.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Native to React Server Componentsno extra libraries needed
  • Automatic caching and deduplication
  • Supports next: { revalidate: N } for ISR
  • Works with the new App Router, Next.jss modern architecture
  • Officially promoted by Vercel as the future of data fetching

Use when: Youre using Next.js 13+ and the App Router. This is now the recommended approach for server-side data fetching in modern Next.js applications.

4. SWR (Stale-While-Revalidate)

SWR is a popular React hook developed by Vercel for client-side data fetching. It follows the stale-while-revalidate caching strategy: return cached data immediately, then revalidate in the background. This creates an instant user experience while keeping data up to date.

Example:

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

export default function Posts() {

const { data, error } = useSWR('/api/posts', fetcher);

if (error) return <div>Failed to load</div>;

if (!data) return <div>Loading...</div>;

return (

<ul>

{data.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Backed by Vercels engineering team
  • Automatic revalidation on focus, network reconnect
  • Optimistic updates and mutation support
  • Lightweight and highly performant
  • Works seamlessly with Next.js API routes

Use when: You need real-time interactivity on the client sidedashboards, comment sections, live feeds. Avoid for SEO-critical content that must be server-rendered.

5. React Query (TanStack Query)

React Query (now TanStack Query) is a feature-rich data fetching library that handles caching, background updates, pagination, and mutations with precision. Its the go-to choice for complex applications with multiple data sources and state dependencies.

Example:

import { useQuery } from '@tanstack/react-query';

const fetchPosts = async () => {

const res = await fetch('/api/posts');

return res.json();

};

export default function Posts() {

const { data, isLoading, error } = useQuery({

queryKey: ['posts'],

queryFn: fetchPosts,

staleTime: 5 * 60 * 1000, // 5 minutes

});

if (isLoading) return <div>Loading...</div>;

if (error) return <div>Error</div>;

return (

<ul>

{data.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Industry-standard for state management in React
  • Extensive documentation and community adoption
  • Powerful caching and deduplication
  • Supports suspense, pagination, and infinite scroll
  • Works with both client and server components (with proper setup)

Use when: Building complex applications with multiple API endpoints, mutation-heavy workflows, or data dependencies. Ideal for enterprise-grade apps where reliability and scalability are paramount.

6. Next.js API Routes + Client-Side fetch()

Next.js API routes allow you to create serverless functions within your app. You can use them as proxies to external APIs, handle authentication, or preprocess data before sending it to the client. Combining them with client-side fetch() gives you full control over data flow.

Example:

API Route (/api/posts.js):

export default async function handler(req, res) {

const response = await fetch('https://external-api.com/posts');

const data = await response.json();

res.status(200).json(data);

}

Client Component:

export default function Posts() {

const [posts, setPosts] = useState([]);

useEffect(() => {

const fetchPosts = async () => {

const res = await fetch('/api/posts');

const data = await res.json();

setPosts(data);

};

fetchPosts();

}, []);

return (

<ul>

{posts.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Keeps API keys and secrets off the client
  • Enables data transformation and caching at the edge
  • Reduces CORS and rate-limiting issues
  • Works with any external service

Use when: You need to secure API keys, transform data, or combine multiple external sources. Avoid if you need SEO or fast initial loadsthis method is client-side only.

7. getStaticPaths + getStaticProps (Dynamic SSG)

When you need to generate static pages for dynamic routeslike blog posts with slugs or product pagesyou combine getStaticPaths with getStaticProps. This method pre-renders each route at build time, ensuring optimal performance and SEO.

Example:

export async function getStaticPaths() {

const res = await fetch('https://api.example.com/posts');

const posts = await res.json();

const paths = posts.map(post => ({

params: { id: post.id.toString() },

}));

return { paths, fallback: 'blocking' };

}

export async function getStaticProps({ params }) {

const res = await fetch(https://api.example.com/posts/${params.id});

const post = await res.json();

return { props: { post } };

}

export default function Post({ post }) {

return <h1>{post.title}</h1>;

}

Why its trusted:

  • Generates static HTML for every dynamic route
  • Supports fallback: 'blocking' for on-demand rendering
  • Perfect for CMS-driven sites (e.g., Contentful, Sanity)
  • Used by nearly all major Next.js blogs and e-commerce platforms

Use when: You have a large number of dynamic routes that need to be indexed by search engines. Avoid if your content changes constantly and you cant afford build time delays.

8. use() Hook with Server Components (Next.js 13+)

Introduced in React 18 and fully supported in Next.js 13+, the use() hook allows you to consume promises directly inside Server Components. Its a lower-level API that enables Suspense for data fetching without requiring a library.

Example:

import { use } from 'react';

async function fetchPosts() {

const res = await fetch('https://api.example.com/posts');

return res.json();

}

export default function Posts() {

const posts = use(fetchPosts());

return (

<ul>

{posts.map(post => <li key={post.id}>{post.title}</li>)}

</ul>

);

}

Why its trusted:

  • Native React APIno third-party dependencies
  • Enables true Suspense boundaries
  • Works with streaming and server components
  • Future-proof and aligned with Reacts direction

Use when: Youre building with the App Router and want minimal abstraction. Best for developers comfortable with Reacts core APIs and who prioritize performance over convenience.

9. Custom Hook with Cache Layer

Building a custom data fetching hook with a local cache (using Map, localStorage, or In-Memory Cache) gives you fine-grained control over how and when data is fetched. This approach is especially useful for reducing redundant requests and improving perceived performance.

Example:

const cache = new Map();

async function fetchData(url) {

if (cache.has(url)) {

return cache.get(url);

}

const res = await fetch(url);

const data = await res.json();

cache.set(url, data);

return data;

}

export function usePosts() {

const [data, setData] = useState(null);

const [loading, setLoading] = useState(true);

useEffect(() => {

fetchData('/api/posts')

.then(setData)

.finally(() => setLoading(false));

}, []);

return { data, loading };

}

Why its trusted:

  • Eliminates duplicate requests
  • Improves performance for repeated calls
  • Can be extended with TTL, invalidation, and error handling
  • Customizable for specific app needs

Use when: You have frequent, low-latency API calls that dont change oftenlike user preferences, settings, or metadata. Avoid for high-velocity or user-specific data that requires freshness.

10. Next.js Middleware + Data Fetching

Next.js Middleware allows you to run code before a request is completed. While not a direct data fetching method, it can be used to pre-fetch or inject data into the request context, which can then be accessed by server components or API routes.

Example (middleware.js):

import { NextRequest, NextResponse } from 'next/server';

export async function middleware(request) {

const res = await fetch('https://api.example.com/config');

const config = await res.json();

request.nextUrl.searchParams.set('config', JSON.stringify(config));

}

Then in a Server Component:

export default function Page({ searchParams }) {

const config = JSON.parse(searchParams.get('config'));

return <div>{config.brandName}</div>;

}

Why its trusted:

  • Runs before page renderingideal for global data
  • Reduces redundant fetches across pages
  • Can be used for A/B testing, localization, or auth
  • Executes at the edge for low latency

Use when: You need to inject global data (like site config, user locale, or auth tokens) across multiple pages without repeating fetches. Avoid for page-specific datause Server Components instead.

Comparison Table

Method Render Type Best For SEO Friendly Performance Caching Next.js Version
getServerSideProps Server-Side Rendering (SSR) User-specific, real-time data Yes Medium None (per request) 10+
getStaticProps + revalidate Static Site Generation (SSG) + ISR Content-heavy sites, blogs, e-commerce Yes High Yes (revalidate) 10+
fetch() in Server Components Server-Side (App Router) Modern Next.js apps (13+) Yes High Yes (automatic) 13+
SWR Client-Side Dynamic UIs, dashboards, real-time feeds No High (after initial load) Yes (stale-while-revalidate) 10+
React Query Client-Side Complex state, mutations, pagination No High Yes (advanced caching) 10+
API Routes + fetch() Client-Side Securing API keys, data transformation No Low (initial load) No 10+
getStaticPaths + getStaticProps Static Site Generation (SSG) Dynamic routes (blog posts, products) Yes High Yes (build-time) 10+
use() Hook Server-Side (App Router) Minimalist, React-native approach Yes High Yes (via fetch cache) 13+
Custom Cache Hook Client-Side Reducing redundant calls, local state No Medium-High Yes (custom) 10+
Middleware + Data Injection Pre-Request Global data, auth, localization Yes (if used in SSR) High Yes (request context) 13+

FAQs

Which data fetching method is best for SEO?

For optimal SEO, use getStaticProps with revalidate or getServerSideProps in the Pages Router, or fetch() inside Server Components in the App Router. These methods render content on the server, ensuring search engines receive fully populated HTML. Avoid client-side-only fetching (like useEffect + fetch) for content-critical pages.

Can I use SWR or React Query with Server Components?

Yes, but with caveats. SWR and React Query are designed for client-side rendering. If you use them inside Server Components, they wont work as intended because Server Components dont support hooks like useEffect or useState. Use them only in Client Components. For server-side data, prefer fetch() or getStaticProps/getServerSideProps.

Whats the difference between getStaticProps and fetch() in Server Components?

getStaticProps is a Pages Router function that runs at build time or on-demand (with ISR). fetch() in Server Components is part of the App Router and runs on every request (unless cached). fetch() supports next: { revalidate: N } for ISR, making it the modern replacement for getStaticProps in Next.js 13+.

Is it okay to fetch data on the client side in Next.js?

Yesbut only for non-SEO-critical content. Client-side fetching is perfect for interactive UIs, user actions, or data that changes frequently after the page loads (e.g., comments, live scores). Never use it for content that must be indexed by search engines or shown on initial load.

How do I avoid hydration errors when fetching data?

Hydration errors occur when server-rendered HTML doesnt match the client-side rendered output. To avoid them:

  • Fetch data on the server using getServerSideProps, getStaticProps, or Server Components
  • Dont use useEffect to fetch data for content that should appear on first load
  • Use loading states consistently on both server and client
  • Ensure your server and client use the same data source and logic

Can I use both getStaticProps and SWR together?

Absolutely. Use getStaticProps to render the initial page with static data, then use SWR to fetch updated data on the client side after hydration. This gives you the best of both worlds: fast initial load and real-time updates.

How does caching work with fetch() in Server Components?

Next.js automatically caches fetch() requests made in Server Components. By default, it caches responses for 10 seconds. You can override this with the next: { revalidate: N } option to enable ISR, or use cache: 'no-store' to disable caching entirely.

Should I use middleware for data fetching?

Use middleware only for global, request-level data like authentication tokens, localization, or site-wide configuration. Dont use it for page-specific content. Middleware runs before every request and can add latency if overused. Prefer Server Components for page-level data.

Whats the future of data fetching in Next.js?

The future is Server Components and fetch(). Vercel and the React team are moving toward a model where data fetching is declarative, server-first, and integrated into the component tree. Client-side libraries like SWR and React Query will still play a role for interactivity, but server-side fetching will become the default standard.

Conclusion

Choosing the right way to fetch data in Next.js isnt about picking the most popular methodits about aligning your strategy with your apps goals. Do you need SEO? Use getStaticProps or Server Components with fetch(). Do you need real-time updates? Reach for SWR or React Query. Are you building a modern app with Next.js 13+? Embrace Server Components and the App Router.

The 10 methods outlined here arent just optionstheyre proven patterns used by top developers and companies worldwide. Each has its place, its strengths, and its trade-offs. The key to building fast, reliable, and scalable Next.js applications lies in understanding those trade-offs and applying the right tool at the right time.

Remember: trust isnt earned by popularity. Its earned through consistency, performance, and adherence to best practices. Whether youre rendering a static blog or a live dashboard, the methods above have been tested under real-world conditions. Use them confidently, optimize deliberately, and always prioritize the user experience.

As Next.js continues to evolve, staying grounded in these foundational patterns will ensure your applications remain performant, maintainable, and future-ready. Start with server-side rendering where possible. Optimize with caching. Enhance with client-side interactivity. And never underestimate the power of a well-fetched piece of data.