Dynamic Gatsby pages at load time with Sanity and URL parameters
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 ArticlePageHow 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 thread118 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.