> ## Documentation Index
> Fetch the complete documentation index at: https://docs.webstorio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing

> Map pages to URLs in webstorio.config.ts — static paths, nested routes, and dynamic {param} wildcards.

Every page in a Webstorio CLI project is a React module registered with a
**slug** in `webstorio.config.ts`. The slug is the URL path on both
`webstorio dev` and the published site.

## Register a route

```ts webstorio.config.ts theme={"dark"}
pages: [
  { file: "pages/home.tsx", slug: "/", title: "Home", type: "system" },
  { file: "pages/about.tsx", slug: "about", title: "About" },
  { file: "pages/blog.tsx", slug: "blog", title: "Blog" },
],
```

| Slug           | URL                                  |
| -------------- | ------------------------------------ |
| `"/"`          | `/` (home — always `type: "system"`) |
| `"about"`      | `/about`                             |
| `"docs/guide"` | `/docs/guide`                        |

* Slugs are path segments separated by `/`. Do not include a leading slash
  except for home (`"/"`).
* Exactly one page should use `slug: "/"`.
* Header and footer are **components** (`_header`, `_footer`), not pages — they
  wrap every route and are not visited as their own URLs.

Full field reference: [Configuration → pages](/cli/configuration#pages).

## Nested routes

Use multiple segments for hierarchy:

```ts theme={"dark"}
{ file: "pages/docs-getting-started.tsx", slug: "docs/getting-started", title: "Getting started" },
```

That serves `/docs/getting-started`. Nesting is only in the slug string — you
do not need a matching folder tree under `pages/` (flat files are fine).

## Dynamic routes (wildcards)

A path segment wrapped in `{…}` is a **wildcard**. One page module handles every
URL that matches the pattern:

```ts theme={"dark"}
{
  file: "pages/blog-detail.tsx",
  slug: "blog/{content_entry_id}",
  title: "Post",
},
```

| Pattern                        | Matches                  | Does not match       |
| ------------------------------ | ------------------------ | -------------------- |
| `blog/{content_entry_id}`      | `/blog/abc-123`          | `/blog`, `/blog/a/b` |
| `store/product/{product_slug}` | `/store/product/mug--id` | `/store/products`    |
| `docs/{section}/{topic}`       | `/docs/api/auth`         | `/docs/api`          |

Rules:

* Param names must be `{name}` where `name` starts with a letter or `_`, then
  letters, digits, `_`, or `-`.
* Segment count must match exactly (no optional trailing segments).
* Exact static routes win over wildcards when both could apply.
* The same rules apply in **`webstorio dev`** and on the **live site**.

### Read params in page code

```tsx pages/blog-detail.tsx theme={"dark"}
import { useEffect, useState } from "react";

export default function BlogDetail() {
  const [entryId, setEntryId] = useState<string | null>(null);

  useEffect(() => {
    const params = window.webstorio.getPathParams();
    setEntryId(params.content_entry_id ?? null);
  }, []);

  // Fetch with getContentEntryById(contentTypeKey, entryId), etc.
  return <main>{entryId}</main>;
}
```

`getPathParams()` returns an object of param name → decoded path segment. It is
available in local preview and on published pages. See
[Client SDK → Dynamic routes](/api-reference/sdk/overview#dynamic-routes).

## Feature page conventions

Dashboard create-page and [`webstorio generate`](/cli/commands#webstorio-generate)
use stable slug patterns so SDK helpers and deep links stay consistent:

| Feature            | Typical slugs                                                      |
| ------------------ | ------------------------------------------------------------------ |
| Content list       | `content/{typeKey}`                                                |
| Content detail     | `content/{typeKey}/{content_entry_id}`                             |
| Content category   | `content/category/{categoryKey}`                                   |
| Form               | `form/{formKey}`                                                   |
| Store              | `store/products`, `store/product/{product_slug}`, `store/checkout` |
| Orders (with auth) | `store/orders`, `store/orders/{order_id}`                          |
| Auth               | `auth/sign-in`, `auth/account`                                     |

You can use custom slugs (for example `blog/{content_entry_id}`) — just keep
list links and `getPathParams()` keys aligned with the names you chose.

## Linking between pages

Use normal `<a href="…">` (or your own router helpers). Paths are site-relative:

```tsx theme={"dark"}
<a href="/blog">All posts</a>
<a href={`/blog/${entry.id}`}>Read more</a>
<a href="/store/product/${encodeURIComponent(slug)}">View product</a>
```

For store product detail, the platform often uses `slug--id` in the
`{product_slug}` segment so the page can resolve by slug or id.

## Related

* [Configuration](/cli/configuration#routing) — slug field details
* [Local development](/developer/local-development) — preview and data modes
* [`webstorio generate`](/cli/commands#webstorio-generate) — scaffold feature routes
* [Client SDK](/api-reference/sdk/overview) — `getPathParams()` and data methods
