Features
Templates
Templates let admins save a set of field values from an existing document and reuse them as a starting point when creating new documents. When templates exist for a collection, the "New" button opens a picker so the author can choose between a blank document or any saved template.
When templates help
Templates are most valuable when your team publishes content that follows a repeating structure but still needs flexibility per document.
- Recurring content patterns — Weekly newsletters, product pages, or event announcements often share the same field structure. A template pre-fills the boilerplate so authors only change what actually differs each time.
- Onboarding new editors — New team members may not know which fields matter or what values are expected. A well-named template ("Standard product page", "Weekly recap") acts as an in-app guide.
- Consistency without rigidity — Unlike mandatory field defaults, templates are suggestions. An author can accept the template values, override individual fields, or ignore the template and start blank. The schema still validates — templates just reduce blank-page friction.
- Faster time-to-publish — Pre-filling common fields (status, category, layout choices) means fewer decisions before an author can start writing.
Templates vs. alternatives
| Approach | When to use |
|---|---|
| Templates | Opt-in starting points for new documents. Authors choose a template at create time; it does not affect existing documents. |
| Cloning an existing document | Copies all fields including content you may not want to carry over. Better when the new document is closely derived from a specific existing one. |
| Field defaults in code | Applied automatically to every new document regardless of context. Use for universal fallbacks, not for collection-specific patterns. |
| Collection presets | Developer-managed configuration baked into the schema. Templates are user-managed and can be created or updated without a code deploy. |
Creating a template
Open any document in a collection and use the Save as Template option in the editor header actions menu. A dialog appears with three fields:
| Field | Required | Description |
|---|---|---|
| Template name | Yes | A short, human-readable label shown in the picker (e.g. Blog post draft). |
| Description | No | Optional text shown below the name in the picker to help authors choose. |
| Set as default template | No | When checked, this template is pre-selected and shown with a "Default" badge. |
Clicking Save template captures the current form values for all non-system fields and stores them against the collection.
Only one template per collection can be the default. Setting a new default does not automatically unset others — manage defaults explicitly via updateTemplate.
Set a default when most new documents in this collection follow the same structure — editors can still choose a different template or start blank.
Using templates
When one or more templates exist for a collection, clicking the New button on the collection list page opens the Create new document dialog instead of navigating directly. The dialog presents:
- Blank document — opens the new document form with all fields empty.
- One button per saved template — opens the new document form pre-filled with that template's field values. Default templates are marked with a "Default" badge.
Selecting a template appends ?templateId=<id> to the new document URL. The document edit page fetches the template and merges its fields map into the empty form, giving the author a head start.
Template values are applied only to new documents (isNew flag). Navigating to an existing document with a templateId query parameter has no effect.
Managing templates
Templates are stored per collection. Use the admin module API to list, update, or delete them programmatically or from custom UI.
Audit log
Every template operation is recorded in the audit log:
| Action | Triggered by |
|---|---|
template.create | createTemplate mutation |
template.update | updateTemplate mutation |
template.delete | deleteTemplate mutation |
Admin module API
The following functions are available on the object returned by createVextroAdminModule.
listTemplatesForCollection
Returns all templates for a given collection, ordered by creation time.
const templates = await convex.query(api.listTemplatesForCollection, {
collectionSlug: "posts",
});
// Array<AdminTemplate> Args
| Arg | Type | Description |
|---|---|---|
collectionSlug | string | The collection to list templates for. |
getTemplate
Returns a single template by ID, or null if not found.
const template = await convex.query(api.getTemplate, {
templateId: "abc123",
});
// AdminTemplate | null Args
| Arg | Type | Description |
|---|---|---|
templateId | string | The ID of the template to fetch. |
createTemplate
Creates a new template for a collection. Returns the new template ID.
const templateId = await convex.mutation(api.createTemplate, {
name: "Blog post draft",
description: "Standard layout with a title, body, and tags pre-filled.",
collectionSlug: "posts",
fields: {
status: "draft",
tags: ["general"],
},
isDefault: false,
}); Args
| Arg | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name shown in the template picker. |
description | string | No | Short description shown below the name. |
collectionSlug | string | Yes | The collection this template belongs to. |
fields | Record<string, unknown> | Yes | Map of field names to pre-populated values. |
isDefault | boolean | No | Whether this is the default template for the collection. |
updateTemplate
Patches an existing template. All patch fields are optional.
await convex.mutation(api.updateTemplate, {
templateId: "abc123",
patch: {
name: "Updated name",
isDefault: true,
},
}); Args
| Arg | Type | Description |
|---|---|---|
templateId | string | The ID of the template to update. |
patch.name | string | New display name. |
patch.description | string | New description. |
patch.fields | Record<string, unknown> | Replacement field values (replaces the entire map). |
patch.isDefault | boolean | Set or unset the default flag. |
deleteTemplate
Permanently deletes a template. This does not affect documents already created from it.
await convex.mutation(api.deleteTemplate, {
templateId: "abc123",
}); Args
| Arg | Type | Description |
|---|---|---|
templateId | string | The ID of the template to delete. |
AdminTemplate type
type AdminTemplate = {
_id: string;
_creationTime: number;
name: string;
description?: string;
collectionSlug: string;
fields: Record<string, unknown>;
createdBy: string; // User ID of the admin who created the template
createdAt: number; // Unix timestamp (ms)
updatedAt: number; // Unix timestamp (ms)
isDefault: boolean;
};