Discussion about creating a catch-all route within the app directory in Next.js and Sanity.io.
pageand
post. My post slugs all start with
/post:
/post/the_post_title
app/[[..slug]]/page.tsx:
import { groq } from 'next-sanity'
import getQueryFromSlug from '@helpers/getQueryFromSlug'
import { sanityClient } from '@lib/sanity.server'
import SinglePage from './single-page'
export async function generateStaticParams() {
const paths = await sanityClient.fetch(groq`*[_type in ["page", "post"] && defined(slug.current)][].slug.current`)
return paths.map((slug: string) => ({
slug: slug.split('/').filter((p) => p),
}))
}
/**
* Helper function to return the correct version of the document
* If we're in "preview mode" and have multiple documents, return the draft
*/
function filterDataToSingleItem(data: object | any, preview = false) {
if (!Array.isArray(data)) {
return data
}
if (data.length === 1) {
return data[0]
}
if (preview) {
return data.find((item) => item._id.startsWith(`drafts.`)) || data[0]
}
return data[0]
}
export default async function Page({ params }: { params: { slug: string[] } }) {
const { slug } = params
const { query, queryParams, docType } = getQueryFromSlug(slug)
const pageData = await sanityClient.fetch(query, queryParams)
const data = filterDataToSingleItem(pageData, false)
return (
<>
{docType === 'page' && <SinglePage data={pageData} />}
</>
)
}helpers/getQueryFromSlug:
import { groq } from 'next-sanity'
const getQueryFromSlug = (slugArray = []) => {
const docQuery = {
post: groq`*[_type == "post" && slug.current == $slug][0] {
title,
_type,
"slug": slug.current,
}`,
page: groq`*[_type == "page" && slug.current == $slug][0] {
title,
_type,
"slug": slug.current,
}`,
}
let docType = ''
const [slugStart] = slugArray
// We now have to re-combine the slug array to match our slug in Sanity.
let queryParams = { slug: `/${slugArray.join('/')}` }
if (slugStart === 'post' && slugArray.length === 2) {
docType = `post`
} else {
docType = `page`
}
return {
docType,
queryParams,
query: docQuery[docType],
}
}
export default getQueryFromSlugsingle-page.tsx:
'use client'
import React from 'react'
interface SinglePageProps {
data: object | any
}
const SinglePage = ({ data }: SinglePageProps) => {
console.log(data)
return (
<>
Page
</>
)
}
export default SinglePageBefore moving to the app dir, I followed this article for the base setup:
https://www.simeongriggs.dev/nextjs-sanity-slug-patterns
/post/post1on this particular project, but I might as well learn the “correct” way to do it to begin with. I will only have /page and /stories/story as slugs. Ive added a snap from my slug route as well, doesn’t seem like i need tooo much altering of my code to make it work? Ignore the : any`s, im learning ts and next at the same time. I will let you know when and if I figure this out! Thank you so much for the example!
/appfolder looks like this
I'm not myself sure that my approach is the "correct" way to do it but I've enjoyed working like that so far, at least for smaller, less complex projects
🙂
order(_updatedAt desc)to get the draft first instead of the original: https://sanity-io-land.slack.com/archives/C9Z7RC3V1/p1678866113734179?thread_ts=1675954847.939669&cid=C9Z7RC3V1 . No need for any parsing in code afterwards 🙂
Was this answer helpful?
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.