DEV Community

Cover image for Data fetching patterns for Next.js & Sanity
Lorenzo Rivosecchi
Lorenzo Rivosecchi

Posted on β€’ Edited on

5

Data fetching patterns for Next.js & Sanity

Next.js and Sanity are a great combination for building websites. Connecting the two technologies together is very simple: download the client library and use it in your code to fetch the data.

The problem i've found when trying to learn these tools is that there are a lot of different ways you can render the data in a Next.js app and it's not always clear what is the right one to pick for your project.

In this blog post i will explain a few patterns that i've learned over the years and explain their strengths and weaknesses.


If you want to skip reading the article and see the ideas in practice you can:


Server Side Rendering (SSR)

This pattern is very simple. Since Sanity provides a CDN for GROQ queries, we can fetch data on the server using getStaticProps without worrying too much about response times. Rendering data on the server per request and adding a cache layer on top of your database is a long established practice that has been recently chosen by Remix.

import sanityClient from '@sanity/client';

const client = sanityClient({
  projectId: 'your-project-id',
  dataset: 'production',
  useCdn: true
})

export default function getServerSideProps({ params }) {
   const query = `[_type == 'page' && slug.current == $slug][0]`;
   const page = client.fetch(query, { slug: params.slug });
   return {
     props: {
       page
     }
   }
}

export default function Page({ page }) {
   return (
     <main>
       <h1>{page.title}</h1>
     </main>
   )
} 
Enter fullscreen mode Exit fullscreen mode

The main benefit of this approach is that is very easy to understand. getServerSideProps runs on every request and the data will always come from the Sanity CDN. Updates to the dataset will become public as soon as the CDN content is replaced.

πŸ‘ Simple behaviour
❌ Slow response
πŸ‘ Errors can be spotted easily
❌ Errors take down the whole page
❌ Dataset needs to be public


Incremental Static Regeneration (ISR)

To mitigate the impact of cold starts on responses we can replace getServerSideProps with getStaticProps so that page props can be stored in a CDN. To avoid presenting stale data for too long is important to keep a low revalidate time.

import sanityClient from '@sanity/client';

const client = sanityClient({
  projectId: 'your-project-id',
  dataset: 'production',
  useCdn: false,
  token: process.env.SANITY_TOKEN
})

export default async function getStaticProps({ params }) {
   const query = `[_type == 'page' && slug.current == $slug && !(_id in path("drafts.**"))][0]`;
   const page = await client.fetch(query, { slug: params.slug });
   return {
     props: {
       page
     },
     revalidate: 60 // 1m
   }
}

export default async function getStaticPaths() {
  const query = `[_type == 'page' && defined(slug.current)].slug.current`;
  const pages = await client.fetch(query);
  return {
    paths: pages,
    fallback: "blocking"
  }
}

export default function Page({ page }) {
   return (
     <main>
       <h1>{page.title}</h1>
     </main>
   )
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘ Fast response on cached pages
❌ Slow response on new pages
πŸ‘ Old content is shown instead of errors
❌ Errors may remain unnoticed
❌ Dataset needs to be public


ISR + Stale While Revalidate (SWR)

A good practice to ensure fast response times without loosing freshness of your data is to revalidate the page props on the client using Javascript. Since Next.js ships a generous amount of javascript to the client we might as well use it at our advantage.

import sanityClient from "@sanity/client";
import useSWR from "swr";

const client = sanityClient({
  projectId: 'your-project-id',
  dataset: 'production',
  useCdn: true,
});

function getPageBySlug(slug) {
  const query = `[_type == 'page' && slug.current == $slug][0]`;
  return client.fetch(query, { slug });
}

export async function getStaticProps({ params }) {
  const page = await getPageBySlug(params.slug);
  return {
    props: {
      initialData: page
    },
    revalidate: 600 // 10m 
  }
}

export default async function getStaticPaths() {
  const query = `[_type == 'page' && defined(slug.current)].slug.current`;
  const pages = await client.fetch(query);
  return {
    paths: pages,
    fallback: true
  }
}

export default Page({ initialData }) {
  const { query } = useRouter();
  const { data } = useSWR(query.slug, getPageBySlug, {
    initialData
  });
   return (
     <main>
       <h1>{data?.title}</h1>
     </main>
   )
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘ Always fast responses
πŸ‘ Data is revalidated on the client
πŸ‘ Errors can be handled on the client
❌ Dataset needs to be public

Neon image

Serverless Postgres in 300ms (!)

10 free databases with autoscaling, scale-to-zero, and read replicas. Start building without infrastructure headaches. No credit card needed.

Try for Free β†’

Top comments (0)

Image of Quadratic

Free AI chart generator

Upload data, describe your vision, and get Python-powered, AI-generated charts instantly.

Try Quadratic free

πŸ‘‹ Kindness is contagious

Dive into this thoughtful article, cherished within the supportive DEV Community. Coders of every background are encouraged to share and grow our collective expertise.

A genuine "thank you" can brighten someone’s dayβ€”drop your appreciation in the comments below!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found value here? A quick thank you to the author makes a big difference.

Okay