Fetching Sanity data in a component outside of /pages for a site-builder setup

8 replies
Last updated: Nov 29, 2025
Wondering what is the cleanest way to fetch data from Sanity into a specific component (so not pulling data into
/pages
).I have a kind of "site-builder" setup where you can drag and drop components to build up custom pages.
And in this case I want to be able to list all the latest posts from
/pages/articles/[slug].js
into a component called something like
ArticleListingComponent.js
using
*[_type == "article"][0...5]
. Has anyone done something like this and wants to give me a few pointers? Thank you
AI Update

For 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.

Pages Router

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...
      })}
    </>
  )
}

2. Client-Side Fetching

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.

For Modular Page Builders

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:

  1. Define your page schema with an array of component blocks
  2. Fetch the page data at the page level (including any data needed by child components)
  3. Map over the blocks and render the appropriate component

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.

Show original thread
8 replies

Sanity – Build the way you think, not the way your CMS thinks

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.

Was this answer helpful?