# Custom page (https://docs.salla.dev/twilight/react/routes/custom-page)



<Callout title="Framework: TanStack Start">
  This page is written for TanStack Start, the default and currently supported framework. [See what depends on the framework](https://docs.salla.dev/twilight/react/start/frameworks).
</Callout>

The customer sees a page the merchant wrote in the dashboard (About us, Shipping policy, Terms): a title, the HTML body, and comments when the page allows them. Customise this route to style the content, use the SEO fields the merchant filled in, or read the page `type`. To add a page the merchant does not manage, see [Add a page](https://docs.salla.dev/twilight/react/recipes/add-a-page).

## URL [#url]

| Pattern                      | Example                |
| ---------------------------- | ---------------------- |
| `/$slug/page-{$id}`          | `/about-us/page-12`    |
| `/{locale}/$slug/page-{$id}` | `/en/about-us/page-12` |

### Params [#params]

| Name     | Type                  | Example    | Description                                             |
| -------- | --------------------- | ---------- | ------------------------------------------------------- |
| `slug`   | `string`              | `about-us` | For readable links. Not passed to the loader.           |
| `id`     | `string`              | `12`       | The page id, written `page-12`.                         |
| `locale` | `string \| undefined` | `en`       | Optional ([locale prefix](https://docs.salla.dev/twilight/react/routes/home#locale-prefix)). |

There are no search params.

## Route file [#route-file]

```tsx title="app/routes/$slug.page-$id.tsx"
// @auto-generated
import { createFileRoute } from '@tanstack/react-router';
import { PageSingle } from '@salla.sa/twilight-theme-engine/routes/page';
import type { PageSingleProps } from '@salla.sa/twilight-theme-engine/routes/page';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';

export const Route = createFileRoute('/{-$locale}/$slug/page-{$id}')({
  loader: ({ params }): Promise<PageSingleProps> =>
    PageSingle.loader({ params: { id: params.id }, locale: params.locale }),
  head: withHead(PageSingle),
  component: PageSingleComponent,
});

function PageSingleComponent() {
  const data: PageSingleProps = Route.useLoaderData();
  return <PageSingle.Component {...data} />;
}
```

To take the page over, write your own file and map the path in `app/routes.ts`: `route('/$slug/page-{$id}', 'page-single.tsx')`. The real theme does this to keep the page `type`, which the engine loader drops. See [Fork a built-in route](https://docs.salla.dev/twilight/react/recipes/fork-a-built-in-route).

### A customised version [#a-customised-version]

This reads the page from the pages API to keep the `type` the engine drops, then reuses the engine head and component.

```tsx title="app/routes/page-single.tsx"
import { createFileRoute } from '@tanstack/react-router';
import { page as pageApi } from '@salla.sa/twilight-theme-engine/api/page';
import { PageSingle, type PageSingleProps } from '@salla.sa/twilight-theme-engine/routes/page';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';

type Data = PageSingleProps & { type?: string };

export const Route = createFileRoute('/{-$locale}/$slug/page-{$id}')({
  loader: async ({ params }): Promise<Data> => {
    const data = await pageApi.findOrThrow(params.id);
    return {
      type: data.type,
      page: {
        slug: 'page-single', // the engine's id for this page
        id: data.id,
        title: data.name,
        url: data.url,
        content: data.content,
        metadata: data.metadata,
        created_at: data.created_at,
      },
    };
  },
  head: withHead(PageSingle),
  component: PageComponent,
});

function PageComponent() {
  const { type, ...data } = Route.useLoaderData();
  return (
    <>
      {type === 'customised' && <p className="container">Custom layout page</p>}
      <PageSingle.Component {...data} />
    </>
  );
}
```

## What it loads [#what-it-loads]

`PageSingleProps` is one `page` object:

| Field                | Description                                    |
| -------------------- | ---------------------------------------------- |
| `slug`               | The page identifier, `page-single`.            |
| `id`, `title`, `url` | From the API (`name` becomes `title`).         |
| `content`            | The HTML body written in the dashboard.        |
| `metadata`           | Optional SEO `title` and `description`.        |
| `created_at`         | Optional `published_time` and `modified_time`. |

The loader runs on the server for the first request. The component renders `content` as raw HTML inside an element with the `content-entry` class (the hook for styling it), and adds the comments widget in the browser. See [Loading data](https://docs.salla.dev/twilight/react/guide/loading-data) and the [page API](https://docs.salla.dev/twilight/react/api/modules/page).

## Head and SEO [#head-and-seo]

Title is the page title and description is the body with tags stripped (not truncated). It does not read `metadata`; to use it, extend the head with `withHead(PageSingle, (head, _ctx, data) => ...)`. Canonical and `og:url` are the page's `url`, the Open Graph type is `article` with published and modified times, and hreflang links are added. There is no JSON-LD. See [SEO and head](https://docs.salla.dev/twilight/react/guide/seo-and-head).

## Edge cases [#edge-cases]

* **Unknown page.** Any API failure becomes a 404 error page, not the full [not-found page](https://docs.salla.dev/twilight/react/routes/not-found).
* **Wrong slug.** Ignored, no redirect. The canonical tag is the page's own `url`.
* **Merchant HTML.** The body is injected as is.
* **Page `type`.** The API returns it, but the engine loader does not pass it on; use the loader above to read it.

## See also [#see-also]

* [Add a page](https://docs.salla.dev/twilight/react/recipes/add-a-page)
* [Fork a built-in route](https://docs.salla.dev/twilight/react/recipes/fork-a-built-in-route)
* [Routes and pages](https://docs.salla.dev/twilight/react/guide/routes-and-pages)
* [Routes overview](https://docs.salla.dev/twilight/react/routes)
