How to Fetch Data in Nextjs

How to Fetch Data in Next.js: A Comprehensive Tutorial Introduction Fetching data efficiently is a fundamental aspect of building modern web applications. In Next.js, a popular React framework, data fetching plays a crucial role in delivering dynamic content, improving user experience, and optimizing performance. This tutorial explores how to fetch data in Next.js, covering its importance, methods

Nov 17, 2025 - 11:34
Nov 17, 2025 - 11:34
 5

How to Fetch Data in Next.js: A Comprehensive Tutorial

Introduction

Fetching data efficiently is a fundamental aspect of building modern web applications. In Next.js, a popular React framework, data fetching plays a crucial role in delivering dynamic content, improving user experience, and optimizing performance. This tutorial explores how to fetch data in Next.js, covering its importance, methods, and best practices to help you build scalable and high-performing applications.

Whether you are rendering pages on the server, client, or using static generation, understanding data fetching in Next.js allows you to choose the right strategy for your use case. This guide will provide step-by-step instructions, real-world examples, and essential resources to master data fetching in Next.js.

Step-by-Step Guide

1. Understanding Next.js Data Fetching Methods

Next.js provides several built-in methods to fetch data depending on the rendering strategy:

  • getStaticProps: Fetch data at build time for static generation.
  • getServerSideProps: Fetch data on each request for server-side rendering.
  • Client-Side Fetching: Fetch data dynamically in the browser using React hooks.

2. Fetching Data with getStaticProps

getStaticProps runs at build time and fetches data before the page is generated. It is ideal for content that does not change frequently and benefits from fast load times.

Example:

export async function getStaticProps() {

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

const data = await res.json();

return {

props: {

data,

},

};

}

function Page({ data }) {

return (

<div>

<h1>Static Props Data</h1>

<pre>{JSON.stringify(data, null, 2)}</pre>

</div>

);

}

export default Page;

3. Fetching Data with getServerSideProps

getServerSideProps runs on every request, enabling server-side rendering with fresh data. It is suitable for dynamic content that changes frequently or requires user-specific information.

Example:

export async function getServerSideProps() {

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

const data = await res.json();

return {

props: {

data,

},

};

}

function Page({ data }) {

return (

<div>

<h1>Server Side Props Data</h1>

<pre>{JSON.stringify(data, null, 2)}</pre>

</div>

);

}

export default Page;

4. Client-Side Data Fetching with useEffect

For interactions that require dynamic fetching after the initial page load, client-side data fetching using Reacts useEffect hook is appropriate. This approach is common for fetching data on user events or when you want to load data asynchronously without blocking the initial render.

Example:

import { useEffect, useState } from 'react';

function ClientSideFetch() {

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

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

useEffect(() => {

async function fetchData() {

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

const result = await res.json();

setData(result);

setLoading(false);

}

fetchData();

}, []);

if (loading) return <p>Loading...</p>;

return (

<div>

<h1>Client Side Fetch Data</h1>

<pre>{JSON.stringify(data, null, 2)}</pre>

</div>

);

}

export default ClientSideFetch;

5. Incremental Static Regeneration (ISR)

Next.js supports Incremental Static Regeneration to update static pages after build time. You can specify a revalidate interval in seconds in getStaticProps to regenerate the page in the background.

Example:

export async function getStaticProps() {

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

const data = await res.json();

return {

props: { data },

revalidate: 60, // Re-generate page every 60 seconds

};

}

6. Handling Errors and Loading States

When fetching data, its critical to handle errors gracefully and provide users with loading indicators. For server-side methods, you can return error statuses or fallback props. For client-side fetching, manage loading and error states using React state.

Example of client-side error handling:

import { useEffect, useState } from 'react';

function ClientSideFetch() {

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

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

const [error, setError] = useState(null);

useEffect(() => {

async function fetchData() {

try {

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

if (!res.ok) {

throw new Error('Network response was not ok');

}

const result = await res.json();

setData(result);

} catch (err) {

setError(err.message);

} finally {

setLoading(false);

}

}

fetchData();

}, []);

if (loading) return <p>Loading...</p>;

if (error) return <p>Error: {error}</p>;

return <pre>{JSON.stringify(data, null, 2)}</pre>;

}

export default ClientSideFetch;

Best Practices

1. Choose the Right Data Fetching Method

Select the data fetching method based on your application's needs. Use getStaticProps for static content, getServerSideProps for dynamic or user-specific content, and client-side fetching for interactions or incremental updates.

2. Optimize API Requests

Minimize the number of API calls and batch requests when possible to reduce latency. Use caching strategies and consider using CDN-based APIs to improve speed.

3. Use Incremental Static Regeneration

Leverage ISR to balance static performance with dynamic content updates. This approach allows you to regenerate pages without rebuilding the entire site.

4. Handle Errors and Loading States

Always implement error handling and loading indicators for a seamless user experience. This improves the reliability and professionalism of your application.

5. Secure Your API Keys

Never expose API keys or sensitive data in client-side code. Use environment variables and server-side methods to keep secrets secure.

6. Leverage TypeScript for Type Safety

If using TypeScript, define interfaces for your fetched data to catch errors early and improve maintainability.

Tools and Resources

1. Next.js Official Documentation

The official Next.js docs provide comprehensive guides on data fetching methods and best practices: https://nextjs.org/docs/basic-features/data-fetching

2. React Query

A powerful library for client-side data fetching and caching, React Query simplifies asynchronous state management: https://react-query.tanstack.com/

3. SWR

Next.js creators recommend SWR, a React Hooks library for data fetching with built-in caching and revalidation: https://swr.vercel.app/

4. Axios

A popular HTTP client for making API requests, Axios offers easy-to-use methods and supports interceptors: https://axios-http.com/

Real Examples

Example 1: Static Blog Posts with getStaticProps

Fetch blog posts from an external API at build time and list them on a static page.

export async function getStaticProps() {

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

const posts = await res.json();

return {

props: { posts },

revalidate: 10, // Update every 10 seconds

};

}

function Blog({ posts }) {

return (

<div>

<h1>Blog Posts</h1>

<ul>

{posts.map(post => (

<li key={post.id}>{post.title}</li>

))}

</ul>

</div>

);

}

export default Blog;

Example 2: Server-Side User Dashboard

Render user-specific data on each request using getServerSideProps.

export async function getServerSideProps(context) {

const { userId } = context.params;

const res = await fetch(https://api.example.com/users/${userId});

const user = await res.json();

if (!user) {

return {

notFound: true,

};

}

return {

props: { user },

};

}

function UserDashboard({ user }) {

return (

<div>

<h1>Welcome, {user.name}</h1>

<p>Email: {user.email}</p>

</div>

);

}

export default UserDashboard;

Example 3: Client-Side Search

Fetch search results dynamically as the user types.

import { useState, useEffect } from 'react';

function Search() {

const [query, setQuery] = useState('');

const [results, setResults] = useState([]);

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

useEffect(() => {

if (!query) {

setResults([]);

return;

}

setLoading(true);

const fetchResults = async () => {

const res = await fetch(/api/search?q=${query});

const data = await res.json();

setResults(data.results);

setLoading(false);

};

fetchResults();

}, [query]);

return (

<div>

<input

type="text"

value={query}

onChange={e => setQuery(e.target.value)}

placeholder="Search..."

/>

{loading && <p>Loading...</p>}

<ul>

{results.map(result => (

<li key={result.id}>{result.name}</li>

))}

</ul>

</div>

);

}

export default Search;

FAQs

Q1: What is the difference between getStaticProps and getServerSideProps?

Answer: getStaticProps fetches data at build time and generates static pages, resulting in faster load times but less frequent updates. getServerSideProps fetches data on every request, providing fresh data but with increased server load and slower response times.

Q2: Can I use both server-side and client-side data fetching in the same Next.js app?

Answer: Yes, Next.js allows combining server-side data fetching for initial page load with client-side fetching for dynamic updates, offering flexibility to optimize performance and user experience.

Q3: How does Incremental Static Regeneration improve performance?

Answer: ISR allows static pages to be regenerated in the background at specified intervals, combining the speed of static pages with the ability to update content regularly without rebuilding the entire site.

Q4: Is it possible to secure API keys when fetching data in Next.js?

Answer: Yes, keep API keys secure by using environment variables and only access them in server-side code such as getStaticProps or getServerSideProps. Avoid exposing secrets in client-side code.

Q5: What libraries can help with client-side data fetching in Next.js?

Answer: Libraries like SWR and React Query provide advanced features like caching, revalidation, and request deduplication, making client-side data fetching more efficient and easier to manage.

Conclusion

Fetching data in Next.js is a versatile and powerful feature that enables developers to build dynamic, fast, and scalable web applications. Understanding the differences between static generation, server-side rendering, and client-side fetching is essential to selecting the best approach for your project.

By following the step-by-step guide, applying best practices, and leveraging helpful tools and resources, you can master data fetching in Next.js to deliver exceptional user experiences. Experiment with different methods, optimize your API calls, and always consider security and error handling to ensure your application performs reliably in production.