Unreleased
Permalinks
Permalink patterns and the permalink engine.
Single posts resolve through a WordPress-style permalink pattern. Supported tokens:
| Token | Source |
|---|---|
{slug} | the post slug (always present) |
{category} | the post's (single) category slug |
{type} | the postType value |
{year} {month} {day} | derived from publishedAt (month/day zero-padded) |
{id} | the post id |
The default is /posts/{slug}. Set a code-level default via posts.permalink; editors can override it at runtime in General Settings → Posts → Permalink pattern.
The engine (buildPermalink / matchPermalink, exported from @systhemaui/payload) is pure and dependency-free. Two important behaviours:
- Build/match symmetry. When any token a pattern references resolves to empty for a post (e.g. a category-less post under
/{category}/{slug}),buildPermalinkfalls back to the default/posts/{slug}rather than dropping the segment — a dropped segment would yield a URLmatchPermalink(exact segment count) couldn't round-trip, so the post would 404 at its own URL.{slug}always resolves, so the default is always buildable. - Pages win. Posts are matched after the exact-path Pages query. A Page at
/blogalways wins over a post whose permalink would also produce/blog.
import { buildPermalink, matchPermalink } from '@systhemaui/payload'
buildPermalink('/{category}/{slug}', { slug: 'golf-r-2025', category: { slug: 'reviews' } })
// → '/reviews/golf-r-2025'
matchPermalink('/{category}/{slug}', '/reviews/golf-r-2025')
// → { tokens: { category: 'reviews', slug: 'golf-r-2025' } }matchPermalink is purely structural — it does not verify the captured values correspond to a real post. The router queries by {slug} (or {id}) then verifies any captured {category}/{type}/date tokens against the result, so a wrong-token URL 404s.