Ver en Español
Fetching Data with Next.js
Apr 11, 2023
Updated: Jun 25, 2026

Fetching Data with Next.js

There are many ways to bring data into our application with Next.js. Depending on when and how you need the information, there will be several alternatives that can work for you.

Before we start: Pages Router and App Router. This post explains the data model of the Pages Router (getStaticProps, getStaticPaths, getServerSideProps). It's still valid if your project uses the pages/ folder, but since Next.js 13 the App Router (the app/ folder) is the recommended paradigm, and those functions do not exist there. Further down you'll find a section with the App Router equivalent. If you're starting a new project on Next.js 16, jump straight to that section.

Let's start with what you already know. If you want to fetch data on the client side, you should keep doing it the way you normally do: using axios, fetch, SWR, or TanStack Query (formerly react-query, now @tanstack/react-query) inside a useEffect or whatever hook you're using. Classes with componentDidMount are a thing of the past; in React 19 the natural pattern is hooks or, even better, Server Components.

Tip: Next.js automatically injects fetch so you can use it in your application.

Now, if your goal is to fetch data on the server side or ahead of time, in the Pages Router we have three options:

  • getStaticProps
  • getStaticPaths
  • getServerSideProps

Static Data

All the methods above are for page prerendering only. You cannot use them in components or to fetch data on the client side.

So let's talk about getStaticProps on a page:

// We create a file called 'index.js' inside the 'pages' folder
// This file defines a function component called 'IndexPage'
const IndexPage = () => {
  // Here you write JSX that defines the content of the 'IndexPage' component
}

// We export the 'IndexPage' component as the default value of this file
export default IndexPage

// We create an async function called 'getStaticProps' used
// in Next.js static page generation
export async function getStaticProps(context) {
  // Here we define the props that will be passed to the 'IndexPage' component
  return {
    props: {}
  }
}

Because this function is written in your page file and exported by default, Next.js will run it at build time, and any result will be passed as a prop to the exported page.

Tip: The results of this function are saved inside a JSON file and passed as a prop to the page component on the client side at runtime.

This function, like the others, only runs on the server side. In fact, not even the code of this function is sent to the client, so you can do very interesting things here:

  • Interact with the file system.
  • Connect to a database.
  • Do expensive backend work.

The context object holds the parameters when a user requests one of our pages and that page is dynamic, meaning it has [id].js or [parameter].js or whatever inside [ ]. That's why the context object exposes those parameters in context.params. But imagine you have a list of posts and you don't know how many there are or which ones, and you still want your site to be static. That's where the getStaticPaths method comes into play. Let's see an example:

// We create a file called '[slug].js' inside the 'blog' folder, within 'pages'
// This file defines a function component called 'IndexPage'
const IndexPage = () => {
  // Here you write JSX that defines the content of the 'IndexPage' component
}

// We export the 'IndexPage' component as the default value of this file
export default IndexPage

// 'getStaticPaths' tells Next.js which dynamic routes to generate statically.
// It returns an object with 'paths' (the routes) and 'fallback' (required).
export async function getStaticPaths() {
  // Important: at build time there is NO origin, so a relative fetch like
  // '/api/posts' fails. Use an absolute URL or read the data directly
  // (file system or database).
  const results = await fetch('https://www.your-site.com/api/posts')
  const posts = await results.json()
  const paths = posts.map(post => ({ params: { slug: post.slug } }))
  // 'paths' must have the shape [{ params: { slug: 'post-slug' } }]
  // 'fallback' is required: false, true, or 'blocking'
  return { paths, fallback: false }
}

// 'getStaticProps' fetches the data for each page at build time
export async function getStaticProps({ params }) {
  // Again, an absolute URL or direct data access on the server
  const res = await fetch(`https://www.your-site.com/api/post/${params.slug}`)
  const post = await res.json()
  return {
    props: { post }
  }
}

Static Paths

If a page has a dynamic route [id].js and uses getStaticProps, it must also use getStaticPaths to prerender all the pages at build time into HTML.

Tip: getStaticPaths must always return a fallback key. Use fallback: 'blocking' (or true) if you have a very large website and don't want to statically prerender allllll the pages at once; in that case, routes you didn't generate at build are rendered on demand. Otherwise, use fallback: false and any route not listed will return a 404.

Server Data

Finally, we have getServerSideProps. This method runs every time the page where you wrote it is visited, so unlike getStaticProps, you'll wait for all the API data on every request, including parameters, headers, and the request and response objects. Let's see an example:

// We create a function component called 'IndexPage'
const IndexPage = () => {
  // Here you write JSX that defines the content of the 'IndexPage' component
}

// We export the 'IndexPage' component as the default value of this file
export default IndexPage

// 'getServerSideProps' runs on every request, on the server side
export async function getServerSideProps() {
  // Here we fetch the data from an external source and convert it to JSON
  const response = await fetch(`https://somedata.com`)
  const data = await response.json()

  // We return an object with 'props' containing the data for 'IndexPage'
  return { props: { data } }
}

And in the App Router (Next.js 13 to 16)

If your project uses the app/ folder, the default paradigm since Next.js 13 and the recommended one in Next.js 16, the three functions above no longer apply. Instead, the components in the app/ folder are Server Components and can be async, so you can fetch directly inside the component. Caching is controlled with the fetch options.

The mental mapping is simple:

  • getStaticProps (static data at build) is replaced by an async Server Component with fetch(..., { cache: 'force-cache' }).
  • getServerSideProps (data on every request) is replaced by an async Server Component with fetch(..., { cache: 'no-store' }).
  • Incremental revalidation (ISR) is achieved with fetch(..., { next: { revalidate: 60 } }).
  • getStaticPaths (which dynamic routes to generate) is replaced by the generateStaticParams function.
// app/blog/[slug]/page.js (App Router)

// Replaces getStaticPaths: defines which slugs are generated statically
export async function generateStaticParams() {
  const posts = await fetch('https://www.your-site.com/api/posts').then(r => r.json())
  return posts.map(post => ({ slug: post.slug }))
}

// The component is an async Server Component: the fetch runs on the server.
// 'force-cache' makes it static (like getStaticProps); 'no-store' makes it
// dynamic per request (like getServerSideProps).
export default async function PostPage({ params }) {
  const { slug } = await params
  const post = await fetch(`https://www.your-site.com/api/post/${slug}`, {
    cache: 'force-cache',
  }).then(r => r.json())

  return <article>{post.title}</article>
}

Note: The fetch caching options and the App Router's default caching behavior have changed across Next.js versions. In particular, fetch stopped caching by default as of Next.js 15 (the default is "auto no cache"); use cache: "force-cache" to cache and next: { revalidate: n } for ISR. Also, in Next.js 15 and later params is a promise and must be used with await.

So, when to use which?

  • Do you need runtime data and do NOT need Server Side Rendering? A: Use fetch or axios on the client side inside a useEffect or the right hook (SWR or TanStack Query).
  • Do you need runtime data that varies from request to request and you DO need Server Side Rendering? A: Use getServerSideProps (Pages Router) or an async Server Component with fetch(..., { cache: 'no-store' }) (App Router).
  • Do you have pages that fetch information that doesn't change much, is cacheable, and is available at build time, for example from a CMS? A: Use getStaticProps (Pages Router) or a Server Component with fetch(..., { cache: 'force-cache' }) (App Router).
  • Same as above, but the page also receives parameters? A: Use getStaticProps with getStaticPaths (Pages Router) or generateStaticParams (App Router).

Tip: Next.js keeps getting better when it comes to data fetching; it's by far my favorite part, since it has little to no overhead and is extremely powerful.

That's all. I hope this post is useful to you and that you can apply it to a project you have in mind, or that it simply helped you understand the mechanisms Next.js offers for data fetching.

3-point summary

  1. In the Pages Router, Next.js offers getStaticProps, getStaticPaths, and getServerSideProps to fetch data on the server side.
  2. getStaticProps and getStaticPaths are used to prerender static and dynamic pages at build time, while getServerSideProps is used for Server Side Rendering on every request.
  3. In the App Router (Next.js 13 to 16) those functions are replaced by async Server Components with fetch (controlling cache and revalidate) and by generateStaticParams. Choose based on whether you need Server Side Rendering, how often the data changes, and whether the information is available at build time.

Leave me a comment if it helped, if you want to add an opinion, or if you have any questions; feel free to drop it below. And remember, if you liked it, you can also share it using the social links below.

Suggested exercises

  1. Create a basic Next.js project and implement an example of getStaticProps to fetch data from a local JSON file.
  2. Modify the previous project and create a page with a dynamic route using getStaticPaths and getStaticProps to generate static pages with specific data (remember to include the fallback key).
  3. Add a function to the project that uses getServerSideProps to fetch data from an external API and show the results on a page.
  4. Rewrite exercise 2 in the App Router: use generateStaticParams and an async Server Component with fetch to achieve the same result.
  5. Implement a pagination function on the page where you used getServerSideProps to limit the number of results shown and allow navigation between pages.
  6. Run performance tests and compare the load and processing times of the pages using each method. Analyze in which cases it's more convenient to use each one.

Good luck with your exercises! Feel free to share your results and questions in the comments. I'll be happy to help if you have any doubts or difficulties along the way. Happy learning!

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias