Define Paths
This article will show you how to do dynamic routing in your Next.js and Prismic project.
Defining dynamic routes is an essential part of building a website with a CMS. As your content creators begin to add their new pages, the web app will need to create URL paths to hit those pages. That’s where routing comes in.
To define routes for Prismic, you will create a routes option in your client config, which describes the routing structure of your Next.js application. Here’s how to define fixed paths, dynamic paths, and nested paths with the routes option:
Fixed paths
If you have a fixed page, like pages/about.js
, you don’t need getStaticParams()
. Next.js already knows the route and the content for the route. However, you still need to define the route in your Prismic routes
option, to enable internal linking.
This is a route in the routes
option for the About page:
const routes = [
{
type: "page",
uid: "about",
path: "/about",
},
];
Dynamic paths
In Next.js’s filesystem-based routing, dynamic path segments are defined inside square brackets: pages/[uid].js
or app/[uid]/page.js
. This route handles any URL with a single path segment, such as /apples
or /carrots
. The square brackets around [uid]
act as a wildcard.
Next.js’s static generation will pre-render all the pages in your website to optimize page speed. To do so, Next.js must know what pages to render. In the Page Router, getStaticPaths()
tells Next.js what pages to render for a dynamic route, such as pages/[uid].js
.
Next.js’ getStaticPaths()
function is called once when your project is built. It will need to know all the possible routes for a page, so using the Prismic client’s getAllByType()
query and the asLink()
function, we can quickly get all the documents of a page type and return that information from the Prismic API to build those paths.
Here’s an example:
import * as prismic from "@prismicio/client";
import { createClient } from "../prismicio";
// Fetch content from prismic
export async function getStaticProps({
params,
previewData,
}) {
const client = createClient({ previewData });
const page = await client.getByUID("page", params.uid);
return {
props: { page },
};
}
// Define Paths
export async function getStaticPaths() {
const client = createClient();
const pages = await client.getAllByType("page");
return {
paths: pages.map((page) => prismic.asLink(page)),
fallback: true,
};
}
Here, getStaticPaths()
tells Next.js to render a page for every page document from Prismic. In the setup step, you defined a route resolver (the routes option). Next.js will generate a route for every document specified with your route resolver.
Defining dynamic paths in the App Router works similarly. generateStaticParams()
tells Next.js the values of the dynamic segments in the route, such as uid
in the route /[uid]
.
import { createClient } from "@/prismicio";
import { notFound } from "next/navigation";
export default async function Page({ params }) {
const client = createClient();
const page = await client
.getByUID("page", params.uid)
.catch(() => notFound());
return <h1>{page.uid}</h1>;
}
export async function generateStaticParams() {
const client = createClient();
const pages = await client.getAllByType("page");
return pages.map((page) => {
return { uid: page.uid };
});
}
Here is the routes option for the above example:
const routes = [
{
type: "page",
path: "/:uid",
},
];
Nested paths
Next.js and Prismic both support nested paths. In Next.js, this is accomplished with filesystem-based routing. In Prismic, this is accomplished with the route resolver.
For example, you could have the file pages/[category]/[uid].js
, which would return a file for the /*/*
URL. That might render a page for /clothing/t-shirt
or /dogs/doberman
. Here’s how that looks in Next.js:
import { createClient } from "@/prismicio";
import { notFound } from "next/navigation";
export default async function Page({ params }) {
const client = createClient();
const page = await client
.getByUID("page", params.uid)
.catch(() => notFound());
return <h1>{page.uid}</h1>;
}
export async function generateStaticParams() {
const client = createClient();
const pages = await client.getAllByType("page");
return pages.map((page) => {
return {
category: page.data.category.uid,
uid: page.uid,
};
});
}
Here is the routes
option for the above example:
const routes = [
{
type: "page",
resolvers: {
category: "category",
},
path: "/:category/:uid",
},
];
For more information about nested paths including how to model them, see the Route Resolver article.
Catch-all paths
Beyond using directories, Next.js also has a catch-all routes option, which looks like pages/[[...uid]].js
. This would render a page for /*
, /*/*
, /*/*/*
, etc. So, you could render a page for both /dogs/doberman
and /poodle
. Here’s how that looks in Next.js:
import { createClient } from "@/prismicio";
import { notFound } from "next/navigation";
export default async function Page({ params }) {
const client = createClient();
/*
* `params.uid` contains an array of each part of the URL separated by a `/`.
* In this example, the last part is the document's UID.
*/
const uid = params.pagePath[params.pagePath.length - 1];
const page = await client
.getByUID("page", uid)
.catch(() => notFound());
return <h1>{page.uid}</h1>;
}
export async function generateStaticParams() {
const client = createClient();
const pages = await client.getAllByType("page");
return pages.map((page) => {
return {
pagePath: page.url.split("/").filter(Boolean),
};
});
}
Prismic’s route resolver allows for optional segments. The following routes
option will conditionally render the :category
segment only if it is present, otherwise omitting it:
const routes = [
{
type: "page",
resolvers: {
category: "category",
},
path: "/:category?/:uid",
},
];
Summary
- Your
routes
option should reflect the folder structure of your Next.js project. - Next.js and Prismic work together to offer advanced routing with conditional segments, internationalization, and nested routes.
- When you have the
routes
option implemented,getStaticPaths()
should look almost the same for every page file.