Custom page

A static content page the merchant creates in the dashboard, such as About us or Terms.

Framework: TanStack Start

This page is written for TanStack Start, the default and currently supported framework. See what depends on the framework.

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.

URL

PatternExample
/$slug/page-{$id}/about-us/page-12
/{locale}/$slug/page-{$id}/en/about-us/page-12

Params

NameTypeExampleDescription
slugstringabout-usFor readable links. Not passed to the loader.
idstring12The page id, written page-12.
localestring | undefinedenOptional (locale prefix).

There are no search params.

Route file

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.

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.

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

PageSingleProps is one page object:

FieldDescription
slugThe page identifier, page-single.
id, title, urlFrom the API (name becomes title).
contentThe HTML body written in the dashboard.
metadataOptional SEO title and description.
created_atOptional 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 and the page API.

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.

Edge cases

  • Unknown page. Any API failure becomes a 404 error page, not the full not-found page.
  • 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

On this page