Next.js - dynamic route
First We have to make folder that will store our dynamic page, f.e. if We want url: “/posts/:id” then:
- create folder “src/app/posts/[postId]”
- in folder [postId] create file page.tsx (and “post.module.scss” for styling)
- then in create page We will get postId from params:
import { Post } from "@/types/Posts";
import { NextPage } from "next";
import style from "./post.module.scss"
import { notFound } from "next/navigation";
type PostPageProps = {
params:{
postId:string; //the same name as folder name = postId
}
}
// fetch post here so We can use it also in metadata
const fetchPost = async (postId:number) => {
const resp = await fetch(`http://localhost:3004/posts/${+postId}`)
// show default Next.js page for not found item [404]
if(!resp.ok && resp.status===404){
throw notFound()
}
if(!resp.ok){
throw new Error("problem with fetching post")
}
const post:Post = await resp.json();
return post
}
// create metadata with data fetched from API
export const generateMetadata = async ({params}:PostPageProps)=>{
const post = await fetchPost(+params.postId) // "+" makes number
return {
title:post.title,
description:`read about ${post.title}`
}
}
// 1st method of props declaration
const PostPage = async ({params}:PostPageProps) => {
// get also data in page (fetch has cache so it is not double execution)
const post = await fetchPost(+params.postId) // "+" makes number
return (
<div>
<h1>Posts {params.postId}</h1>
<p>{post.body}</p>
{post.tags && post.tags.length>0 && (
<div className={style.tag}>
{post.tags.map((tag)=> (<em key={tag}>{tag}</em>))}
</div>
)}
</div>
);
};
export default PostPage;
We can declare props also this way:
// 2nd method of props declaration
const PostPage:NextPage<PostPageProps> = async ({params}) => {
const post = await fetchPost(+params.postId)
return (
<div>
<h1>Posts {params.postId}</h1>
<p>{post.body}</p>
{post.tags && post.tags.length>0 && (
<div className={style.tag}>
{post.tags.map((tag)=> (<em key={tag}>{tag}</em>))}
</div>
)}
</div>
);
// };
export default PostPage;
OK. Some styling:
.tag {
margin-top: 20px;
em {
margin-right: 10px;
background-color: bisque;
border-radius: 3px;
padding: 5px;
}
}
Now: We can define path that will CATCH ALL paths nad give us them in params.
How:if We make folder with brackets in name f.e. “[…path]”, then We will catch all paths from url address and app-router will redirect to page.tsx, that is defined in this folder. For example:
folder “app/[…path]”
- url: “localhost:3000/something” will redirect to page in folder […path] when there will be no other folder named “something”.
folder “/app/info/[…path]”
- url: “localhost:3000/info/whatever” will redirect to page in folder /info/[…path] when there will be no other folder named “whatever”.
- url: “localhost:3000/info” will not work this way - only when there will be slash on the end (localhost:3000/info/”) it will redirect to “/info/[…path]”, without slash it will still redirect to “app/[…path]”
folder “/app/info/[[…path]]” - app-router will redirect also with url: “localhost:3000/info” to page in folder: “/app/info/[[…path]]” (and of course all other urls will do the same f.e. “localhost:3000/info/3/4/wtf/667” etc)
How to read params from such urls?
Let’s see example:
- “app/[…path].page.tsx”
import { NextPage } from "next";
type InfoPageProps = {
params: {
path: string[];
};
};
const InfoPage: NextPage<InfoPageProps> = ({ params }) => {
return (
<div>
<h1>Page</h1>
params: {params.path.join("/")}
</div>
);
};
export default InfoPage;
Ok, with “[[…path]]” there is small difference: We need to check if params.path exist, so file “/app/info/[[…path]]” will be as below:
import { NextPage } from "next";
type InfoPageProps = {
params: {
path: string[];
};
};
const InfoPage: NextPage<InfoPageProps> = ({ params }) => {
return (
<div>
<h1>Info / Page</h1>
params: {params.path && params.path.join("/")}
</div>
);
};
export default InfoPage;
That’s all!
*INFO: This post is my notes based on Udemy course (“Nextjs 14 od podstaw”) by Jarosław Juszkiewicz