RFC: Templates API for content #16515
paulpopus
started this conversation in
Feature Requests & Ideas
Replies: 1 comment
-
|
I think it should be called templates anyway, despite the naming conflict. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
RFC: Templates API
TL;DR
Add a first-class Templates API to Payload core that lets editors save the data of any document, block, or field as a reusable template, and pre-fill new content from it. Same primitive at three levels: collection documents, blocks, and field values. Designed to extend later into a richer reusable-content / shared-content system.
The problem
Editors using Payload to build content-heavy products — articles, landing pages, marketing sites, ecommerce listings, knowledge bases — almost always end up repeating the same structures over and over. A "Recipe" article has the same hero, ingredients block, steps block. A "Case Study" page has the same intro, quote, two-column layout, CTA. A "Product" page has the same gallery → spec sheet → related-products composition.
Today, the workflows for "I want to start from a known structure" all have sharp edges:
defaultValueis static and lives in code — non-technical editors can't define or change it.There is no Payload-native way to say "this exact configuration of fields/blocks is a starting point I want to reuse" — at the document level, at the block level, or at the field level.
Why we should care
This is one of the most-requested capabilities we hear from production teams. It comes up in:
Every other block-based CMS in the space — Sanity, Storyblok, Contentful, Webflow CMS, Notion, Framer — exposes some version of this primitive. They build it themselves when we don't ship it. We can do it better than the per-project versions, in core, in a way that composes with the rest of Payload.
It's also the natural foundation for the reusable-content story we want to tell longer-term (see Future direction).
Proposal
Mental model
A template is a named, saved value that can seed an entity. Three entity tiers, increasing in granularity:
blocksfieldarrayandblocks; extensible to any field)All three tiers share one storage collection, one config surface, one set of hooks, and one set of UI primitives. Adding a new tier later (e.g. groups, rich text trees) is purely additive.
Naming — open question
We're calling this "Templates" in the RFC because that's the term users already use when they ask for it. It does conflict with the existing
templates/folder in the repo (which is project starters, unrelated). We'd love feedback on alternatives — possibilities that came up:A clean name matters more once this becomes the foundation for reusable content (where "templates" and "library" start meaning different things). Calling this out early so we can land on a name we won't regret.
Storage:
payload-templatesA new internal collection following the
payload-*convention used bypayload-preferences,payload-locked-documents,payload-jobs, etc. —admin.hidden: trueby default, fully extensible (hooks, access, custom admin components) like any other collection.Why a single JSON field rather than per-collection typed templates: templates need to span heterogeneous shapes (a Page template and an Article template have nothing structurally in common), and field/block templates store fragments rather than full docs. JSON keeps the storage trivial; validation and typing happen at apply time, where the target collection's schema is the source of truth.
Config surface
Templates are opt-in per collection — same shape as
versions/upload/auth:Block-level opt-in (tier 2) is implicit: if a
blocksfield hastemplates: true, every block type inside it can be saved/inserted as a template. (We can add a per-block-type opt-out if there's demand.)Apply semantics
Applying a template is a merge with the same lifecycle as
duplicateFromIDin the existing create operation. Concretely:defaultValues resolve first.dataoverlays on top of those defaults.data(from the form / API caller) overlays on top of the template.beforeValidate/ field hooks then run as normal.This ordering matters: the user is always in control, and field-level defaults still apply for anything the template doesn't touch.
For tier 1 (collections), the apply path piggybacks on the existing
duplicateFromIDplumbing increate.ts— we add a siblingtemplateIDparameter that sources frompayload-templatesinstead of from a sibling document.For tier 2/3, the merge happens client-side in the form state (the same place "duplicate row" already produces a new row).
UI integration
Tier 1 — collection list view. The existing "Create New" button (
ListCreateNewDocButton) becomes a split-button whentemplates.createis enabled: primary action stays "Create New"; the chevron opens a list of available templates plus a "Manage templates" link. The doc edit view gains a "Save as Template" action in the document header menu whentemplates.saveis enabled.Tier 2 / Tier 3 — blocks and array fields. Both share a single integration point: the existing
ArrayActionrow menu (the popover behind the…icon on every block/array row). Two new items: "Save as Template" and "Insert from Template". Because bothBlocksandArrayalready render throughArrayAction, this is one change with two-tier reach. Tier 3 (whole-field replacement) shows up at the field-header level rather than the row level.Selection UI. A "Choose template" drawer that mirrors the existing block-picker (
BlockSelector) — searchable, groupable by category, thumbnailable down the road. Filters automatically byentityType+entitySlugso apostseditor only seespoststemplates, an "image-gallery" block only sees "image-gallery" templates, etc.Access and permissions
Default rule: if you can create in collection
X, you can use templates for collectionX. Same idea for blocks/fields nested insideX. Saving a new template requires create-permission onpayload-templates(default: same condition).payload-templatesexposes the standardaccessAPI like any other collection, so teams can layer on whatever they need: per-team scoping, "personal vs shared" scoping, role-based visibility, lock-down for non-editors, etc. The collection's hooks and admin components are also extensible — we explicitly inherit the same extensibility surface thatpayload-jobsandpayload-query-presetsalready prove out.Open design questions (the discussion part)
These are the calls we'd most like community input on before locking the design.
payload-templates. Probably yes — Payload's drafts machinery is right there, and templates evolve. Worth confirming.enapplied to adedocument — copy theendata verbatim, leave fields blank, or refuse? Tentative answer: copy verbatim and let the editor translate; flag in the UI.beforeApplyTemplatehook for cases that need cloning.templates: trueenough to enable Tier 2/3 inside that collection's blocks/array fields, or should each field opt in individually? Tentative answer: collection-level enables everything; per-fieldtemplates: falseopts out.payload-query-presets. If we end up calling this "Presets", there's already a presets collection for list-view queries. Either we rename one, or we pick a name that doesn't collide.blocksfield whose schema doesn't include that block type, do we silently skip, hard-error, or coerce? Probably hard-error.Future direction (out of scope for v1)
The shape above is deliberately compatible with two follow-on capabilities we want to keep the door open for:
payload-templatescollection (or whatever we end up calling it) can grow amode: 'snapshot' | 'reference'field. Inreferencemode, the consuming document stores a pointer to the template, and edits to the template propagate to every reference. This is the headline ask behind "global blocks" / "shared content" / "reusable sections" — and it's a natural Tier 4 on the same primitive.We are not committing to either of these in v1. The v1 surface is small, additive, and shippable on its own. We're calling out the trajectory so reviewers can pressure-test the proposed shape against where it's headed.
What we're asking for in this discussion
Beta Was this translation helpful? Give feedback.
All reactions