Dynamic Routes

Dynamic Routes and Route Parameters

A file name in square brackets is a parameter: book/[id].tsx answers /book/1 through /book/6, and any other value. The catalog's BookRow calls router.push({ pathname: '/book/[id]', params: { id } }) from useRouter(); an href object lets the router build and escape the URL. The detail screen reads the parameter back with useLocalSearchParams():

src/app/book/[id].tsx: reading the route parameter (excerpt)Shell
export default function BookScreen() {
  const { id } = useLocalSearchParams<'/book/[id]'>();   // always a string
  const book = books.find((b) => b.id === Number(id));
  const { add } = useSession();
  console.log('book screen, id =', JSON.stringify(id));
  if (!book) return <Text style={{ margin: 16 }}>No book with id {id}</Text>;

The root layout logs usePathname() whenever it changes. Tapping Salt and Saffron, Add to cart and Back printed (the second render comes from the cart update):

Output of 146
 LOG  path: /
 LOG  book screen, id = "3"
 LOG  path: /book/3
 LOG  book screen, id = "3"
 LOG  path: /

The catalog passed the number 3, and the screen received the string "3", because parameters travel as URL text. Convert them yourself, and handle values that match no book: a deep link can carry /book/abc. Extra params keys become the query string (/book/3?from=search). Prefer useLocalSearchParams() to useGlobalSearchParams(), which re-renders every mounted screen on each change. A catch-all [...slug].tsx receives an array of segments, which suits nested paths such as /genre/fiction/2025.