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:
- Look at the code repository
- Try the demo
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>
)
}
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>
)
}
👍 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>
)
}
👍 Always fast responses
👍 Data is revalidated on the client
👍 Errors can be handled on the client
❌ Dataset needs to be public
Top comments (0)