Docs

This page isn't translated yet

Create a custom page template

Register a CMS page shell with a root content field and client live preview.

On this page

GoalLink to this section

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 entryLink to this section

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 shellLink to this section

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 configLink to this section

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

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 registrationLink to this section

If the content uses project blocks, preserve livePreview.clientSetup and the client registration. 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 workLink to this section

  • 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 and Preview templates for the complete contract.