---
title: "Create a custom page template"
description: "Register a CMS page shell with a root content field and client live preview."
requested_language: fr
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/fr/guides/recipes/custom-page-template
version: unreleased (main)
docs_index: https://docs.systhema.app/fr/llms.txt
---
> This page isn't translated yet. Showing English.


## Goal

Register a Landing template while keeping the page body editable as native blocks. Use a different shell only when the default template cannot express the page's requirements.

## 1. Define the fields and template registry entry

```ts title="src/page-templates/Landing/index.ts"
import type { SysthemaPayloadPageTemplate } from '@systhemaui/payload'
import { rootLexicalEditor } from '@systhemaui/payload/lexical/editors'
import { LandingView } from './component'

export const landingTemplate: SysthemaPayloadPageTemplate = {
  name: 'landing',
  label: 'Landing',
  component: LandingView,
  clientView: LandingView,
  fields: [{ name: 'content', type: 'richText', editor: rootLexicalEditor() }],
}
```

Template `fields` are grouped under the template name, so this content is `data.landing.content`. Keep `landing` stable after pages use it.

## 2. Render the shell

```tsx title="src/page-templates/Landing/component.tsx"
'use client'

import type { ComponentProps } from 'react'
import { Article } from '@systhemaui/next'
import { RichText } from '@systhemaui/payload/next/client'

type LandingData = {
  landing?: { content?: ComponentProps<typeof RichText>['data'] }
}

export function LandingView({ data }: { data: LandingData }) {
  const content = data.landing?.content

  return (
    <main id="main-content">
      <Article theme="default" layoutBackground="main">
        {content && <RichText data={content} />}
      </Article>
    </main>
  )
}
```

The same presentational component accepts `{ data }` for normal rendering and client preview. Its root content carries section-level blocks, so it has no Section wrapper around RichText. This intentionally small template is a starting point for a different shell.

Keep database queries in the template's `resolveData` hook and pass serializable results through `aux` when you need them. Do not fetch with `getPayload` inside this client module.

## 3. Register in the existing config

Import `landingTemplate` into `src/payload.config.ts`. Add `customPageTemplates: [landingTemplate]` to the existing `userSysthemaConfig`, retaining its other entries.

```bash
pnpm sync
pnpm exec tsc --noEmit
```

Create a page and select Landing in Admin. Add a Section block to its Content field. Leave the catch-all route untouched; `SysthemaPage` chooses the registered template.

## 4. Include custom preview registration

If the content uses project blocks, preserve `livePreview.clientSetup` and the [client registration](https://docs.systhema.app/fr/guides/build-a-site/custom-block.md). Built-in converters are supplied by Systhema; custom converters need registration in the browser.

A custom converter reached from a client view must be browser-safe. Test the server-rendered published path as well as the preview path, especially if it imports an interactive component.

## Check your work

- Landing appears in the page template selector.
- Saved content lives under the intended template group.
- Both public rendering and unsaved live preview work.
- The template has one main and one Article.
- Root blocks supply their own Section bands.

See [Page templates](https://docs.systhema.app/fr/payload/content/page-templates.md) and [Preview templates](https://docs.systhema.app/fr/payload/frontend/live-preview/templates.md) for the complete contract.
