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

# Page URLs

Shopping experiences live on URLs. Customers bookmark them, search engines index them, social posts link to them. Every one of those URLs needs to resolve to something renderable — and it should do so in a single request.

That's what a Page URL is: you hand Frontic a URL-key, and you get back everything needed to render the page — the page type, the data, and all the route metadata. No server-side routing logic, no second lookup, no stitching.

Manage your pages in the [API Builder](https://app.frontic.com/builder/pages) section of the admin app.

<Screenshot name="api-builder/pages-overview" caption="Page URLs in the admin app" alt="Pages section showing a list of page types with their names, connected blocks, and generated URL count" />

<Frame caption="Page URLs in the admin app">
  <img src="https://mintcdn.com/frontic/hS7rYTW0C-o2HHTZ/images/api-builder/pages-overview-light.png?fit=max&auto=format&n=hS7rYTW0C-o2HHTZ&q=85&s=47ef689e21593ca30b6dd440cc55e577" className="dark:hidden block" alt="Pages section showing a list of page types with their names, connected blocks, and generated URL count" width="2880" height="1800" data-path="images/api-builder/pages-overview-light.png" />

  <img src="https://mintcdn.com/frontic/hS7rYTW0C-o2HHTZ/images/api-builder/pages-overview-dark.png?fit=max&auto=format&n=hS7rYTW0C-o2HHTZ&q=85&s=a5898577a01677860e63f2c85d363fcc" className="hidden dark:block" alt="Pages section showing a list of page types with their names, connected blocks, and generated URL count" width="2880" height="1800" data-path="images/api-builder/pages-overview-dark.png" />
</Frame>

<Note>
  Page URLs don't hold their own data — they map a URL pattern to a [Detail Block](/api-builder/blocks), and that block's records become the page's content. Make sure the block you want to serve already exists and is connected to a Data Storage before creating a page.
</Note>

## Create a page

From the [API Builder](https://app.frontic.com/builder/pages), click **New Page**.

<Screenshot name="api-builder/page-create" caption="Create a new Page URL — name, block, and slug field" alt="Create page form with fields for page name, block selection, and slug field configuration" />

<Frame caption="Create a new Page URL — name, block, and slug field">
  <img src="https://mintcdn.com/frontic/hS7rYTW0C-o2HHTZ/images/api-builder/page-create-light.png?fit=max&auto=format&n=hS7rYTW0C-o2HHTZ&q=85&s=677286262ed204d93354d13dfc183a76" className="dark:hidden block" alt="Create page form with fields for page name, block selection, and slug field configuration" width="2880" height="1800" data-path="images/api-builder/page-create-light.png" />

  <img src="https://mintcdn.com/frontic/hS7rYTW0C-o2HHTZ/images/api-builder/page-create-dark.png?fit=max&auto=format&n=hS7rYTW0C-o2HHTZ&q=85&s=49b1feefd165ee89b12c92fd6a32a6c5" className="hidden dark:block" alt="Create page form with fields for page name, block selection, and slug field configuration" width="2880" height="1800" data-path="images/api-builder/page-create-dark.png" />
</Frame>

Three pieces of information define a page:

* **Page name** — a human-readable name (e.g. `Product Detail`). Frontic derives the page type identifier and endpoint slug from this, so use normal capitalized words with spaces.
* **Block** — the [Detail Block](/api-builder/blocks) that shapes the response for this page type
* **Slug** — the block field used to generate the URL (e.g. `name`, `breadcrumbs`, `handle`)

<Tip>
  If no field on your block produces the URL shape you want, add a dedicated slug field and compute it with the [Value Composer](/data-integration/value-composer). Common patterns: `slugify` on a product name, `concat` a category path with a product name and color variant, or combine a brand prefix with a SKU — full control over URL shape without reshaping the underlying data.
</Tip>

### Localized URLs

If the slug field points at a translatable or scoped storage field, Frontic folds out one URL per locale and region from the same record. A single product can resolve at `/en/shop/equipment`, `/de/shop/ausstattung`, and a B2B-scoped `/pro/shop/equipment` at the same time — each served from the [domain](/project-settings/domains) that matches, each resolving to its own [request context](/api-builder/overview#request-context). Translate or scope the slug field on your Data Storage and the URL structure follows.

### Action on withdrawal

Pages change — blocks get removed, records get deactivated. In commerce, URLs that disappear cost you rankings, inbound links, and customer trust. Configure what happens when a page's underlying record is no longer available:

| Action               | Description                                           |
| -------------------- | ----------------------------------------------------- |
| 302 Redirect to home | Temporary redirect — the most common choice           |
| 301 Redirect to home | Permanent redirect, removes the URL from search index |
| Delete URL           | Removes the URL entirely (callers get a 404)          |
| Keep URL with 404    | Keeps the URL but serves a 404 — useful for analytics |

Withdrawn URLs aren't lost — they're detached from the record but kept in the URL history. If a record with the same slug reappears later, Frontic automatically re-attaches the existing URL to it, preserving the original link. A record recreated with a different slug gets a fresh URL, and the old one stays withdrawn.

<Tip>
  To inspect individual generated URLs, verify canonical status, or remove stale entries, use the [URL Browser](/commerce-concepts/urls-seo#url-browser) in the admin app.
</Tip>

### Conflict resolution

URLs must be unique. When two records would generate the same slug (e.g. two products with the same name), pick a strategy:

| Strategy     | Description                                                            |
| ------------ | ---------------------------------------------------------------------- |
| Append token | A secondary field (e.g. SKU) is appended to the slug to make it unique |
| Dismiss      | The second page isn't created — the colliding record gets no URL       |

### Sitemaps

You can augment your sitemaps with your Frontic page URLs. Enable sitemap visibility in your page settings to include that page in a generated sitemap:

<Screenshot name="api-builder/pages-sitemap" caption="Page configuration - Enable sitemap visibility" alt="Page Settings showing the Visible in Sitemap toggle" view="modal" />

<Frame caption="Page configuration - Enable sitemap visibility">
  <img src="https://mintcdn.com/frontic/1mx6IQXJDQLGJfJ5/images/api-builder/pages-sitemap-light.png?fit=max&auto=format&n=1mx6IQXJDQLGJfJ5&q=85&s=b488abc1e7aebe3a219cc2b95bde5a5d" className="dark:hidden block mx-auto mt-5 mb-5" style={{ maxWidth: "480px" }} alt="Page Settings showing the Visible in Sitemap toggle" width="1640" height="668" data-path="images/api-builder/pages-sitemap-light.png" />

  <img src="https://mintcdn.com/frontic/1mx6IQXJDQLGJfJ5/images/api-builder/pages-sitemap-dark.png?fit=max&auto=format&n=1mx6IQXJDQLGJfJ5&q=85&s=b5599a0104fe8b8283b9500782c75f3b" className="hidden dark:block mx-auto mt-5 mb-5" style={{ maxWidth: "480px" }} alt="Page Settings showing the Visible in Sitemap toggle" width="1640" height="668" data-path="images/api-builder/pages-sitemap-dark.png" />
</Frame>

Sitemaps can be fetched via [the Fetch API's sitemap endpoint](/reference/fetch-api/sitemap).

## How URLs resolve

Frontic stores pages by their content-only slug, then projects them through your project's domain mappings to produce one fully-qualified URL per `(scope, region, locale)` combination that's configured to expose the content.

### URL anatomy

A resolvable URL splits into two parts: the **domain** the project mapped, and the **slug** the page record produced.

```
www.demo-shop.com/de   /shop/ausstattung
└──────────────────┘   └───────────────┘
       domain                slug
```

The **domain** is whatever string the project configured under [Domains](/project-settings/domains) — Frontic doesn't model a separate locale prefix layer. The same project can map any of these as a domain entry, and Frontic treats the whole thing as opaque on lookup:

* `www.demo-shop.com` — single domain, single locale
* `de.demo-shop.com` — locale on a subdomain
* `www.demo-shop.com/de` — locale on a path prefix, on the same domain

Each domain entry pins its own scope, region, and locale defaults. The slug is whatever path the page record's slug field produced — translated independently when the field is translatable.

The same product can resolve at all of:

* `www.demo-shop.com/shop/equipment` — public scope, en-US
* `www.demo-shop.com/de/shop/ausstattung` — public scope, de-DE (translated slug, locale carried by the path prefix in the configured domain)
* `b2b.demo-shop.com/shop/equipment` — b2b scope, en-US

Each is a separate URL with its own [request context](/api-builder/overview#request-context); the response shape is identical, but the data inside reflects the resolved scope / region / locale.

### Resolution chain

When a request hits Frontic with a URL like `www.demo-shop.com/de/shop/ausstattung`, the pipeline runs in this order:

1. **Domain match** — Frontic finds the longest configured project-domain entry that owns this URL (e.g. `www.demo-shop.com/de`, not `www.demo-shop.com`). The match returns the scope, region, and locale that domain entry is pinned to.
2. **Context resolution** — the `(scope, region, locale)` tuple becomes the [request context](/api-builder/overview#request-context). If the caller sent an `fs-context` header, that takes precedence; otherwise the domain's defaults apply.
3. **Page lookup** — Frontic strips the matched domain prefix and looks the remaining slug up in the project's URL history for the resolved scope. Hits return the page record; misses fall through to `route.code: 404` (or a redirect, depending on the page's withdrawal action).
4. **Block render** — the matched page's connected [Detail Block](/api-builder/blocks) renders the response, applying the resolved context to translatable / scoped fields.
5. **Route metadata** — Frontic attaches `route` to the response with the resolved code, the active context, alternate URLs for the same content in other regions/locales, and (when relevant) `redirect` or `context.suggested` entries.

The frontend doesn't need to know the chain — it's enough to pass the incoming URL to `client.page()`. The pipeline produces a deterministic answer, and your match-all route renders whatever comes back via `page.type` and `page.data`.

## Fetch a page

Your frontend hits a match-all route with only the incoming slug (e.g. `/fr/femme/chemises`). Pass that slug to Frontic and render the result.

<Tabs>
  <Tab title="Client SDK">
    ```ts theme={"theme":"css-variables"}
    const { slug } = useRoute().params;
    const page = await client.page(slug);
    ```
  </Tab>

  <Tab title="Nuxt Module">
    ```vue theme={"theme":"css-variables"}
    <script setup lang="ts">
    const { slug } = useRoute().params
    const { page } = useFronticPage(slug as string)
    </script>

    <template>
      <component :is="page?.type" :data="page?.data" />
    </template>
    ```
  </Tab>
</Tabs>

See the [Client SDK reference](/reference/client-sdk#client-page) for the full API.

## Response

The response carries the page type, the block payload for this URL, and route metadata.

```json theme={"theme":"css-variables"}
{
  "type": "CategoryDetail",
  "block": "CategoryPage",
  "data": {
    "slug": "gloves-0194bcdaa2d57dc4bb4a9f33ed6b718c",
    "title": "Gloves"
  },
  "route": {
    "code": 200,
    "context": { "region": "de", "locale": "de-DE" },
    "alternates": [
      {
        "slug": "shop/equipment",
        "path": "/en/shop/equipment",
        "url": "www.demo-shop.com/en/shop/equipment",
        "href": "https://www.demo-shop.com/en/shop/equipment",
        "region": "en",
        "locale": "en-GB"
      }
    ]
  }
}
```

<ResponseField name="type" type="string">
  The page type (e.g. `"CategoryDetail"`, `"ProductDetail"`). Use this to pick which component to render.
</ResponseField>

<ResponseField name="block" type="string">
  The underlying Detail Block name. Useful if you map components to block names instead of page types.
</ResponseField>

<ResponseField name="data" type="object">
  The block's content for this URL. Structure depends on the Detail Block configuration.
</ResponseField>

<ResponseField name="route" type="object">
  Route metadata — response code, resolved context, alternates, suggestions, redirects. See [Route metadata](#route-metadata) below.
</ResponseField>

### Route metadata

The `route` property tells your frontend what HTTP code to return, what locale/region was resolved, where to redirect, and which alternate URLs exist for the same content.

<ResponseField name="code" type="number">
  The HTTP status code your frontend should return: `200`, `301`, `302`, or `404`.
</ResponseField>

<ResponseField name="context" type="object">
  The resolved region and locale the page was served for. Use this for analytics, canonical tags, or client-side state.
</ResponseField>

<ResponseField name="alternates" type="array">
  Alternate routes for the same content in other regions/locales. Use for `hreflang` tags and language switchers. The current route is not included.
</ResponseField>

<ResponseField name="redirect" type="object">
  When `code` is `301` or `302`, this holds the target URL to redirect to.
</ResponseField>

<ResponseField name="context.suggested" type="object">
  When the caller's [context](/api-builder/overview#request-context) doesn't match the route Frontic resolved, this suggests a better-matching alternate. See [Context mismatches](#context-mismatches).
</ResponseField>

### Context mismatches

When a visitor with a `contextKey` tied to one region or locale lands on a URL from a different one — say, a user with `de-DE` context hitting `/en/shop/equipment` — Frontic serves the requested URL (never guesses silently) but adds `context.suggested` to the route, containing the matching alternate (slug, path, url, href, region, locale).

Your frontend decides what to do: prompt the user ("we noticed you're in German — switch?"), auto-redirect, adjust the canonical tag, or ignore the suggestion entirely and render the content as-is. Suggestions only appear on `200` responses — redirects and 404s don't include them.

### Error handling and redirects

The HTTP call itself returns `200 OK` whenever Frontic can satisfy the request — logical redirects and not-found cases for the URL live inside `route.code`. Your frontend reads that field, returns the matching HTTP status to the visitor, and (for `301` / `302`) uses `route.redirect.href` as the location. One code path handles every case.

## API Playground

<Note>Preview — the API Playground is in early access. It runs against your live backend today, but the UI and feature set are still settling before general availability. [Reach out](mailto:support@frontic.com) if you want to try it.</Note>

Open the playground from the Pages list header or from any routed page's detail header to look up a rendered page against real data without writing code. Enter a URL, pick a release stage, set the request context, and hit **Send** — the full page response appears side by side with your request.

<Screenshot name="api-builder/playground-page" caption="API Playground for the page endpoint — enter a URL, send, inspect the response" alt="API Playground dialog for the page endpoint showing a URL input on the left, release stage selector, domain and context configuration, and a JSON response with the resolved page type, data, and route metadata on the right" view="modal" />

<Frame caption="API Playground for the page endpoint — enter a URL, send, inspect the response">
  <img src="https://mintcdn.com/frontic/YiN8HmiGFMy2ycGP/images/api-builder/playground-page-light.png?fit=max&auto=format&n=YiN8HmiGFMy2ycGP&q=85&s=c48573a5e7d259f8c07dc766109196d3" className="dark:hidden block mx-auto mt-5 mb-5" style={{ maxWidth: "480px" }} alt="API Playground dialog for the page endpoint showing a URL input on the left, release stage selector, domain and context configuration, and a JSON response with the resolved page type, data, and route metadata on the right" width="2304" height="1532" data-path="images/api-builder/playground-page-light.png" />

  <img src="https://mintcdn.com/frontic/YiN8HmiGFMy2ycGP/images/api-builder/playground-page-dark.png?fit=max&auto=format&n=YiN8HmiGFMy2ycGP&q=85&s=efc3fcf1c79537805c469827ffc4a6db" className="hidden dark:block mx-auto mt-5 mb-5" style={{ maxWidth: "480px" }} alt="API Playground dialog for the page endpoint showing a URL input on the left, release stage selector, domain and context configuration, and a JSON response with the resolved page type, data, and route metadata on the right" width="2304" height="1532" data-path="images/api-builder/playground-page-dark.png" />
</Frame>

Use the playground to:

* **Resolve a URL** — paste the full URL (e.g. `www.demo-shop.com/de/products/red-sneaker`) and see which page type, block, and data Frontic resolves for it
* **Pick a release stage** — run the request against `develop`, `preview`, or `public`. Preview and public only appear when those environments exist on the project
* **Inspect the response** — status, response time, and payload size are shown alongside the body so you can spot performance issues. The response includes `type`, `block`, `data`, and the full `route` metadata
* **Copy code snippets** — auto-generated from your current configuration, ready to paste into your project

## Link to a page from a block

Block responses can embed a URL that points to a page. Add a [Route field](/api-builder/blocks#route) to a block, pick the target page and the field that carries the record key, and Frontic resolves the matching URL for every response — in the caller's locale and region, under the right domain. A product card rendered on `demo-shop.com/de` gets `/de/turnschuhe/air-max`, the same card on `demo-shop.com/en` gets `/en/sneaker/air-max`. No URL building on the frontend, no locale mapping tables.

## Related

<CardGroup cols={2}>
  <Card title="Detail Blocks" icon="cube" href="/api-builder/blocks">
    Shape the response for each page type.
  </Card>

  <Card title="Menu Trees" icon="list-tree" href="/api-builder/trees">
    Build hierarchical navigation from Page URLs.
  </Card>

  <Card title="URLs, Redirects & SEO" icon="link" href="/commerce-concepts/urls-seo">
    How URLs are generated, how history is preserved, and how SEO metadata flows.
  </Card>

  <Card title="Request Context" icon="compass" href="/api-builder/overview#request-context">
    How scope, region, and locale shape every page response.
  </Card>
</CardGroup>
