---
title: "Post types and templates"
description: "Post types and custom post templates."
requested_language: sk
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/sk/payload/posts/post-types
version: unreleased (main)
docs_index: https://docs.systhema.app/sk/llms.txt
---
> This page isn't translated yet. Showing English.


A post carries **two independent selectors**:

- **`postType`** — a taxonomy value (`article`, `video`, …) used for filtering, archives, and queries. Options come from `posts.types` (default `false` → single implicit `article`). A single type hides the select.
- **`postTemplate`** — which registered template renders the post. Options come from the post-template registry. A single `default` template hides the select.

The post-template registry mirrors the page-template registry exactly. The module ships a built-in **Default** Single-Post template; consumers add more via the top-level `customPostTemplates` option (or `posts.templates`). Each registered template contributes a per-template **conditional field group** to the Posts collection, shown when `postTemplate` equals that template's name **or** when `postType` equals it — the same `prepareTemplateFields` / `syncPageTemplates` mechanism Pages use.

> [!TIP]
> **Collapsing the two selectors (1:1 type ↔ template).** When your types and templates line up one-to-one and share names (e.g. types `article`/`video`/`podcast` with templates of the same names), the two selects are redundant. Because a template's conditional field group also reveals when `postType` matches its name, you can drive everything off the single `postType` select: hide the `postTemplate` field (`admin.hidden`) and set `postTemplate = postType` in a `beforeValidate` collection hook so the right template still renders. The editor then picks **one** "type", and its fields appear live. (For decoupled setups where a type can use several templates, keep both selects.)

```tsx
// src/post-templates/VideoPost.tsx  — a custom post template
import type { SysthemaPostTemplate } from '@systhemaui/payload'
import { postHeroFields, postBodyField } from '@systhemaui/payload'

export const VideoPostTemplate: SysthemaPostTemplate = {
  name: 'video',
  label: 'Video',
  component: VideoPostComponent, // your React component rendering the post
  // These fields appear only when postTemplate === 'video':
  fields: [
    ...postHeroFields, // reuse the shipped (optional) hero field shape
    { name: 'videoUrl', type: 'text', required: true },
    { name: 'videoDuration', type: 'text' },
    postBodyField, // the region-level Lexical body field
  ],
}
```

```ts
// src/payload.config.ts — register it (mirrors customPageTemplates)
withSysthema(
  {
    posts: {
      enabled: true,
      types: ['Article', 'Video'],
    },
    customPostTemplates: [VideoPostTemplate],
  },
  baseConfig,
)
```

`SysthemaPostTemplate` is `{ name, label?, component, fields?, hero? }` — the post-template equivalent of `SysthemaPayloadPageTemplate`. The shipped field shapes are re-exported so custom templates can reuse them: `defaultPostTemplateFields`, `postHeroFields` (hero defaults to `heroType: 'none'`, so a content-only post is valid), and `postBodyField` (the region-level Lexical editor body — narrower than the page root editor; no Page/Component blocks).

## The default post template

The built-in `DefaultPost` (`@systhemaui/payload/next`) renders the post's **category** as a muted kicker (`.post-type`) above the hero title and its **tags** as a `<Chip>` row in the hero `append` slot. Both default ON and are gated by the hero config `show.kicker` / `show.tags` (no dedicated General-Settings toggle) with `classNames.kicker` / `classNames.tags` overrides. A content-only (no-hero) post surfaces the kicker + byline + tags standalone above the body. Related posts render through the next-optimized `<PostsList>` (auto `next/image` via `sizesForLayout`) inside a `<Section padding={0}>`.
