Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Dynamic segments and params

Each blog post has its own address: /posts/1, /posts/2 and so on. You cannot create a route per post, so part of the address is made dynamic. A dynamic segment starts with a colon, and its value goes into the params object.

A route with a param

Add a route to app/routes.ts inside the layout:

// app/routes.ts
route("posts/:postId", "routes/post.tsx"),

And create app/routes/post.tsx:

// app/routes/post.tsx
import type { Route } from "./+types/post";
 
export default function Post({ params }: Route.ComponentProps) {
  return <h1>Post number {params.postId}</h1>;
}

Open http://localhost:5173/posts/7 — "Post number 7" appears. params.postId is a string, and TypeScript knows about it thanks to the generated types: a typo like params.id is flagged immediately.

Linking to the route

The link address is built from the param value:

// app/routes/home.tsx
import { Link } from "react-router";
 
export default function Home() {
  const ids = [1, 2, 3];
 
  return (
    <ul>
      {ids.map((id) => (
        <li key={id}>
          <Link to={`/posts/${id}`}>Post {id}</Link>
        </li>
      ))}
    </ul>
  );
}

The home page shows three links, each leading to its own post page.

Several params

An address can have several segments:

// app/routes.ts
route("authors/:authorId/posts/:postId", "routes/author-post.tsx"),

For /authors/anna/posts/3, params contains authorId with "anna" and postId with "3".

Optional segment and splat

PatternMatching addressesValue
:postId/posts/5params.postId
:lang?/about and /en/aboutparams.lang or undefined
*/files/a/b.txtparams["*"], here a/b.txt

A route("*", "routes/not-found.tsx") at the end of the list catches all unknown addresses — the 404 page.

The useParams hook

In a nested component that does not receive params as props, read them with the useParams() hook from react-router. In the route module itself the params prop is more convenient: it is precisely typed.

Common mistake

A param is always a string. params.postId === 7 is never true, and looking up a post by a numeric id finds nothing. Convert the value explicitly with Number(params.postId) and check the result, because anything can be typed into the address, for example /posts/abc.

Thanks for reading!

Donate to the site — we work hard for you.