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

8 replies
Last updated: Mar 31, 2022
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
Until React Server Components mature, this is tough to recommend because all your fetches will be client-side only.
Unfortunately for now I believe the best solution is to do one mega fetch of data at getServersideProps and prop drill it down…

Hi User, thanks for the reply, (love your blog posts on sanity/next). This is where my newness to Next falls short.
Edit: Just puzzled it all together, I thought there was a clever way to bypass prop drilling from the pages folder, but its not that bad once I figured it out. Just passing the _type == article into my global query did the trick
I agree with
user T
. The best solution still be to fetch all necessary data in the getStaticProps. But you can of course encapsulate the data loading per component with DataLoader interface. So you can register a DataLoader per Component that that can be used in the SiteBuilder in the DataLoaderRegistry. And then in your getStaticProps function you iterate over all registered DataLoaders and loads (fetches) the data. This is how i have implemented it
user P
any chance you can share a repo for me to frankenstein together?
user E
Unfortunately not. It's a private project we have implemented for the customer.
Of course no problem, thanks anyways.
don't know if it's help but in my pages file i have /blog page where i fetch all my blogi use
getStaticProps
, i'm only load the 5 first one , and i have a pagination button , if the user click on it i get five more post ( pages/api/loadFiveMorePosts ) .But i a user want to read one article i use
getServerSideProps
.No sur if it's helpful or not for you
😕
Thank you
user V
that is helpful to know. Currently I have a React contextProvider that pushes my blog posts globally and I can use it at will. But its clever to know I can use the
/pages/api/
as well.

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?