Guides

Templates: start a Document from a Dokumentvorlage

The Dokumentvorlagen folder, the placeholders expandTemplate fills in ({{title}}, {{date}}, {{time}}, {{cursor}}), and the templateId of the Document create route.

A Dokumentvorlage is an ordinary Document that sits in a folder named Dokumentvorlagen (TEMPLATES_FOLDER, @skaile/forge-editor/core). A new Document can start as a copy of it: the editor server copies the Dokumentvorlage's content and fills in the placeholders in every text node. The Dokumentvorlage itself is never changed. There is no separate template store, so a Dokumentvorlage has the same access rules, history and editor as any other Document, and a user who may read it may create from it.

Placeholders

expandTemplate(text, { title, now, locale }) is pure and framework-free. It returns { text, cursor }.

PlaceholderBecomes
{{title}}the title of the new Document
{{date}}the date, medium style in locale (25.10.2026)
{{date:short}}, {{date:medium}}, {{date:long}}the date in that Intl.DateTimeFormat style
{{date:iso}}the date as 2026-10-25 (the local day of now)
{{time}}the time, short style in locale (14:05)
{{cursor}}nothing; cursor holds its offset in the expanded text. Every further {{cursor}} is removed too

Names are not case-sensitive and may have spaces inside the braces ({{ Title }}). Anything else in double braces stays exactly as written, an unknown name ({{author}}) and an unknown format ({{date:weird}}) included, so a Dokumentvorlage can carry text that only looks like a placeholder. Block templates for the AI (BlockTypeAi.contentTemplate) keep their own {key} syntax; the two are not mixed, and Templater scripts are not translated.

import { expandTemplate } from '@skaile/forge-editor/core'

const env = { title: 'Sitzung', now: new Date(2026, 9, 25, 14, 5), locale: 'de-DE' }
expandTemplate('# {{title}} ({{date:iso}})\n\n{{cursor}}', env)
// { text: '# Sitzung (2026-10-25)\n\n', cursor: 24 }

expandTemplateContent(content, env) applies the same expansion to every text node of TipTap JSON. It keeps marks, attributes and block IDs, returns a new tree and drops the cursor, because a new Document has no caret yet.

Creating from a Dokumentvorlage

POST /api/editor/documents takes an optional templateId next to title and folder. In the client it is backend.documents.create({ title?, folder?, templateId? }).

const sitzung = await backend.documents.create({ title: 'Sitzung', folder: 'Projekte', templateId: protokoll.id })

The server checks the Dokumentvorlage before it creates anything: the Document must be listed in a folder named Dokumentvorlagen (templatesOf(tree), any depth) and the user must be allowed to read it. It then creates the Document through DocumentAdmin.createDocument and stores the expanded content with storeContent, so the Block index follows as for any write. {{title}} is the title the Document got, which is the default title when the request names none. The date and time use the first language tag of the Accept-Language header, de when there is none.

AnswerWhen
201 with the DocumentSummarycreated
400templateId is an empty string, or the body is otherwise invalid
403the user may not read the Dokumentvorlage
404templateId names no Document inside a Dokumentvorlagen folder, or the folder is unknown
501the documents port has no createDocument or no listTree

The content of the Dokumentvorlage is the live one when the collaboration server has it open, so an unsaved edit is part of the copy. Block IDs are copied unchanged: they are unique per Document only, so the copy and the Dokumentvorlage do not clash.

In the host

The host lists the Dokumentvorlagen from the tree it already has: templatesOf(tree) returns the Documents of the first folder named Dokumentvorlagen, newest first, as DocumentSummarys. The reference app shows them in the menu „Aus Dokumentvorlage“ next to „Neues Dokument“ on app/pages/documents/index.vue and passes the choice as templateId:

import { templatesOf } from '@skaile/forge-editor/core'

const templates = templatesOf(tree) // [{ id, title, … }]
await backend.documents.create({ templateId: templates[0]!.id })
Copyright © 2026