
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeFor your site-builder setup, you have a few clean options depending on whether you're using Next.js App Router or Pages Router:
If you're on the App Router, you can fetch data directly in your component if it's a Server Component (the default):
// ArticleListingComponent.js
import { client } from '@/sanity/lib/client'
export default async function ArticleListingComponent() {
const articles = await client.fetch(
`*[_type == "article"][0...5]`,
{},
{ next: { revalidate: 60 } } // Optional: ISR
)
return (
<div>
{articles.map(article => (
<div key={article._id}>{article.title}</div>
))}
</div>
)
}This is the cleanest approach - each component fetches exactly what it needs, and Next.js automatically deduplicates identical requests during rendering.
If you're using the Pages Router, you have two main approaches:
Since getStaticProps only works at the page level, fetch all data your page builder needs in your page component and pass it down:
// pages/[slug].js
export async function getStaticProps({ params }) {
const page = await client.fetch(`*[_type == "page" && slug.current == $slug][0]`,
{ slug: params.slug }
)
// Fetch additional data for specific components
const articles = await client.fetch(`*[_type == "article"][0...5]`)
return {
props: { page, articles },
revalidate: 60
}
}
function Page({ page, articles }) {
return (
<>
{page.components.map(component => {
if (component._type === 'articleListing') {
return <ArticleListingComponent articles={articles} key={component._key} />
}
// other components...
})}
</>
)
}For truly dynamic components, you can fetch on the client side using the Sanity client with React hooks:
// ArticleListingComponent.js
import { useEffect, useState } from 'react'
import { client } from '@/sanity/lib/client'
export default function ArticleListingComponent() {
const [articles, setArticles] = useState([])
useEffect(() => {
client.fetch(`*[_type == "article"][0...5]`)
.then(setArticles)
}, [])
return (
<div>
{articles.map(article => (
<div key={article._id}>{article.title}</div>
))}
</div>
)
}This works but loses the SEO benefits of pre-rendering. You could also use picosanity for a smaller bundle if you only need read operations.
Since you mentioned a drag-and-drop page builder, you're likely implementing something similar to Sanity's content blocks pattern. The cleanest approach is:
This keeps data fetching centralized and predictable, which is especially important for static generation. If specific components need fresh data, you can use Incremental Static Regeneration with the revalidate option to keep content reasonably up-to-date without full client-side fetching.
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store