How to retrieve Sanity data for meta tags across all Next.js pages?

14 replies
Last updated: Jan 28, 2026
Hello! I'm setting up a NextJS app with Sanity, and am wondering about how to retrieve values for the
<meta>
tags. It looks like Next only allows
getStaticProps
or
getServerSideProps
to be run on individual pages, not the
_app
or
_document
pages. Will I need to run the same query and apply meta tags on every page? Seems like there must be a single place to grab this data and include it on every page 🤔
AI Update

Good question! You're right that this seems inefficient, but there are actually several approaches to handle site-wide metadata in Next.js with Sanity without duplicating queries on every page.

Modern Approach: Next.js App Router with Layouts

If you're using Next.js 13+ with the App Router, you can fetch global metadata in your root layout.tsx file. Unlike _app.tsx in the Pages Router, layouts in the App Router support data fetching directly:

// app/layout.tsx
import { client } from '@/sanity/lib/client'

async function getGlobalSettings() {
  return await client.fetch(`*[_type == "siteSettings"][0]{
    title,
    description,
    ogImage
  }`)
}

export async function generateMetadata() {
  const settings = await getGlobalSettings()
  
  return {
    title: settings.title,
    description: settings.description,
    openGraph: {
      images: [settings.ogImage],
    },
  }
}

export default async function RootLayout({ children }) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}

The App Router's React Server Components allow you to fetch data at the layout level, and you can use Next.js's generateMetadata function to set meta tags.

Pages Router Approach: Global Settings Pattern

If you're using the Pages Router (with _app.tsx and _document.tsx), you have a couple of options:

1. Create a "siteSettings" singleton document in Sanity and fetch it on each page, but pass it down through props to avoid re-querying:

// pages/index.tsx
export async function getStaticProps() {
  const [pageData, globalSettings] = await Promise.all([
    client.fetch(`*[_type == "page" && slug.current == "/"][0]`),
    client.fetch(`*[_type == "siteSettings"][0]`)
  ])
  
  return {
    props: { pageData, globalSettings }
  }
}

2. Use React Context to share global settings across components after fetching them once per page load.

3. Fetch in _app.tsx using getInitialProps (though this disables automatic static optimization):

// pages/_app.tsx
MyApp.getInitialProps = async (appContext) => {
  const globalSettings = await client.fetch(`*[_type == "siteSettings"][0]`)
  return { globalSettings }
}

This last approach works but has performance tradeoffs since it opts your entire app out of static generation.

The cleanest approach is typically to:

  1. Create a siteSettings singleton document type in Sanity
  2. Fetch it alongside your page-specific data in each page's getStaticProps/getServerSideProps
  3. Use Next.js's next/head component to set meta tags per page

While it means including the query on each page, Sanity's Content Lake is fast, and with proper caching/revalidation, the overhead is minimal. Plus, you maintain the benefits of static generation.

If you're starting fresh, I'd strongly recommend the App Router approach—it's much more ergonomic for this exact use case!

Show original thread
14 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?