Learning Next.js 14 I’ve been reading through the documentation on Dynamic Routes. If I’m targeting all pages in my site based on slug
but I want to render something in particular for let’s say a contact
or about
page which approach is accurate?
If my structure is:
app
[slug]
page.tsx
/contact
page.tsx
/about
page.tsx
blog/[slug]
page.tsx
the file system works. In some reading I’ve noticed that page
a conditional is used so it the correct approach to do:
import {Fragment} from 'react'
import { notFound } from 'next/navigation'
type Props = {
readonly params: {
slug: string
}
}
export default async function Pages({params}: Props) {
if (params?.slug === 'about') return <About />
if (params?.slug === 'contact') return <Contact />
if (params?.slug === null) return notFound();
// async run query
return // removed logic
}
so that would lead me to:
app
[slug]
page.tsx
contact.tsx
about.tsx
blog/[slug]
page.tsx
Research
- Dynamic Routes
- Parallel Routes
- Intercepting Routes
- How to implement redirects in Next.js
In Next.js 14 what is the correct file structure to handle pages that match a particular slug?
2
Taken from https://nextjs.org/docs/app/building-your-application/routing/colocation
Apart from routing folder and file conventions, Next.js is unopinionated about how you organize and colocate your project files.
There is no hard-and-fast rule as to how you organize your project, NextJS leaves it up to you to decide based on your requirements.
The first approach seems to make sense to me because contact and about pages are not dynamic. If contact and pages are not dynamic, I don’t think they should be part of [slug]
routes.
The first approach’s structure is also commonly used and understandable. I’d say this approach is better as it keep it simple, stupid (KISS principle).
The second approach can be confusing to other developers
app
[slug]
page.tsx
contact.tsx
about.tsx
On first glance, it seems to mean there are routes like
/subject-1
, /subject-1/contact
, /subject-1/about
/subject-2
, /subject-2/contact
, /subject-2/about
But in fact, there is additional code to control which component is rendered when /[slug]
is accessed.
export default async function Pages({params}: Props) {
if (params?.slug === 'about') return <About />
if (params?.slug === 'contact') return <Contact />
if (params?.slug === null) return notFound();
// async run query
return // removed logic
}
The above code is unnecessary because file-based routing would have already handled it elegantly (as in the first approach).