Dynamic Gatsby pages at load time with Sanity and URL parameters

118 replies
Last updated: Nov 29, 2025
Hello everyone 🙂
Using Gatsby, I need to load in content at
load time not build time which I agree goes against the idea of Gatsby being static content but the client has dropped this on me at the final hour and I don’t have much choice.
The current URL structure is like this:
<http://www.example.com/category/the-article-slug|www.example.com/category/the-article-slug>
but if you visit that, you get a 404 for
category/the-article-slug.js
— ideally here it would somehow load
article.js
and pass it the
/category/
and
/the-article-slug
to fetch with the sanity client
My other option was to change the URL structure like this:
<http://www.example.com/article?category=category&slug=the-article-slug|www.example.com/article?category=category&slug=the-article-slug>
but this loses some of its clean appeal and isn’t ideal
I posted this on Stack Overflow but the reply I got seemed to think it’s impossible

https://stackoverflow.com/questions/66699757/dynamic-pages-at-load-time-not-build-time
AI Update

I understand your predicament! You're trying to fetch Sanity content at runtime in Gatsby rather than at build time, while maintaining clean URLs like /category/the-article-slug. This is definitely possible, though it does go against Gatsby's typical static generation approach.

The matchPath Solution

The Stack Overflow response saying it's impossible is incorrect. Gatsby has a matchPath feature specifically for client-only routes that will solve your problem.

When you create your page in gatsby-node.js, specify a matchPath that matches your dynamic URL pattern:

// gatsby-node.js
exports.createPages = async ({ actions }) => {
  const { createPage } = actions
  
  createPage({
    path: '/article', // The actual page component path
    matchPath: '/:category/:slug', // This matches your URL pattern
    component: require.resolve('./src/templates/article.js'),
  })
}

Accessing Parameters and Fetching Content

In your article.js template, access the route parameters and fetch from Sanity at runtime:

// src/templates/article.js
import React, { useEffect, useState } from 'react'
import sanityClient from '@sanity/client'

const client = sanityClient({
  projectId: 'your-project-id',
  dataset: 'production',
  useCdn: false, // Use false for fresh data
  apiVersion: '2023-01-01'
})

const ArticlePage = ({ location }) => {
  const [content, setContent] = useState(null)
  
  useEffect(() => {
    // Extract category and slug from the URL
    const pathParts = location.pathname.split('/').filter(Boolean)
    const [category, slug] = pathParts
    
    // Fetch from Sanity at load time
    const query = `*[_type == "article" && category->slug.current == $category && slug.current == $slug][0]`
    
    client.fetch(query, { category, slug })
      .then(data => setContent(data))
      .catch(err => console.error(err))
  }, [location.pathname])
  
  if (!content) return <div>Loading...</div>
  
  return (
    <article>
      <h1>{content.title}</h1>
      {/* render your content */}
    </article>
  )
}

export default ArticlePage

How matchPath Works

The matchPath parameter tells Gatsby: "When someone visits any URL matching /:category/:slug, serve the article.js component and let it handle the routing client-side." This is exactly what you need - clean URLs with runtime content fetching.

Important Considerations

SEO Impact: Since content loads at runtime on the client, search engines won't see the content in the initial HTML. This is a significant SEO disadvantage compared to static generation.

Performance: You'll lose the performance benefits of pre-rendered pages, as each visit requires a client-side API call to Sanity.

Alternative Approaches (if you have any flexibility):

  • Gatsby DSG (Deferred Static Generation): Generate pages on first request, then cache them
  • Gatsby SSR: Server-side render pages on each request with full SEO benefits
  • Next.js with Sanity: Better support for dynamic content with ISR or SSR, maintaining both clean URLs and SEO

The matchPath approach will work perfectly for your /category/the-article-slug URL structure while fetching content at load time. This is a documented Gatsby feature designed exactly for client-only routes and SPA-like behavior within Gatsby sites.

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