Dynamic Routes

Wrap a folder name in brackets to capture a path segment.

FolderMatchesrouter.params
app/blog/[slug]/page.jsx/blog/hello{ slug: "hello" }
app/docs/[...path]/page.jsx/docs/a/b/c{ path: ["a", "b", "c"] }

[param] matches one segment; [...rest] is a catch-all matching the remainder.

Reading params

Params are exposed on the reactive router:

JSX
import { router } from "@opentf/afw";

export default function Post() {
  return <h1>Post: {router.params.slug}</h1>;
}

Reading router.params inside a binding keeps the view in sync as you navigate between matching routes.

Generating static paths

Export getStaticPaths() from a dynamic page to list its URLs for prerendering:

JSX
export function getStaticPaths() {
  return [{ params: { slug: "hello" } }, { params: { slug: "hello world" } }];
}

Pass raw parameter values: the framework encodes URL segments and decodes them when matching a route. For catch-all routes, pass an array such as { params: { path: ["guides", "getting started"] } }.

Missing parameters, empty segments, invalid values and duplicate URLs fail the build with an error. getStaticPaths() must return an array. It describes URLs; props entries are unsupported. Use a route loader and router.data to supply page data during prerendering, SSR and navigation.

Per-path metadata

Export generateMetadata({ params }) on a dynamic page when each path needs its own <title>, description, or share image. With a configured prerender target, pair it with getStaticPaths so every enumerated path is pre-rendered with the correct head. A dynamic route without getStaticPaths is skipped at build time but still resolves generateMetadata on each request through an app-provided request renderer / SSR. See Metadata & SEO.

Last updated on
Edit this page