Discussion about creating a catch-all route within the app directory in Next.js and Sanity.io.
14 replies
Last updated: Mar 17, 2023
N
Has anyone made a catch all route within the app dir?
H
Yes, I made one yesterday, I'll try to provide a base example!
H
This example assumes you have two document types:
This might be a lot to take in, please let me know if you get stuck on a certain step somewhere.
Before moving to the app dir, I followed this article for the base setup:
https://www.simeongriggs.dev/nextjs-sanity-slug-patterns
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
N
Takk Henrik! Ill take a look riiight now .
N
Do you still have a page.tsx in the root folder, or is the [[..slug]]/page.tsx used to catch everything including the index page? I have a page builder where everything is modular and my current setup is like this image here, I am trying to wrap my head around the best structure for it, the nesting is not going further than
/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!
H
Aaa sorry, I catch my index route there as well, should've added that to the example I provided
H
My
/appfolder looks like this
H
You seem very close to make it work, let me know if I can assist you more.
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
🙂
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
🙂
N
Yeah ive been loving the appdir, really nice to do everything inside the component 🙂
N
struggling a bit to get the dynamic fetch to work, i got it to work doing it like in the picture, but this is perhaps considered bad form? feels a bit hacky.
N
Thanks Henrik, made it work!
H
Awesome, glad to hear that! 🤟 👏
N
If anyone from the future needs some ideas, ill supply some of my code for a modular setup in the appdir, now on to the next problem, the preview! 😆
J
I recently ran into the same issue where I wanted the latest draft returned, and not the original when doing previews. I used
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 🙂
N
Nice Jon Espen! Ill take a look, thanks! 🙂
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.