Skip to content

Author a schema for your metadata standard

Your metadata standard already exists, even if it only lives in a style guide or in people’s heads: “every page needs a type,” “timestamp should be an ISO date,” “API pages must declare a version.” A JSON Schema turns those agreements into a file that docmeta can enforce on every commit. This page walks through writing that schema, from a minimal one-field example up to the fuller built-in OKF schema, and shows how a document points itself at the schema you wrote.

docmeta validates against standard JSON Schema. If you can write JSON, you can write a docmeta schema; there is no docmeta-specific schema format to learn. A schema describes the frontmatter object: which keys are required, what type each value should be, and whether unexpected keys are allowed.

Here is the smallest schema worth shipping. It says: the metadata must be an object, it must have a title, and that title must be a non-empty string.

title.schema.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["title"],
"properties": {
"title": { "type": "string", "minLength": 1 }
}
}

This is the extra.schema.json fixture that docmeta’s own test suite runs, so you can trust it compiles and validates exactly as shown. Four things are doing the work:

  • "$schema" at the top names the JSON Schema dialect this file is written in: here, draft 2020-12. docmeta reads this line to pick the right validation engine. (This is the schema’s own dialect declaration. It is different from the $schema key you put in a document to choose a schema, covered below.)
  • "type": "object" says the metadata is a set of key/value pairs, which frontmatter always is.
  • "required": ["title"] is the list of fields that must be present. A document missing title fails validation.
  • "properties" describes individual fields. title must be a string, and minLength: 1 rejects an empty title: that a contributor left blank.

A document with this frontmatter passes:

---
title: Getting started
---

A document with empty or missing title fails. That is the whole loop: declare the shape, and docmeta holds every file to it.

The single most important decision in a metadata schema is which fields are required and which are merely recommended. JSON Schema models this with one list:

  • Required: list the field in "required". Its absence is a hard failure.
  • Recommended (optional): describe the field in "properties" but leave it out of "required". If the field is present, docmeta checks its type and format; if it’s absent, that’s fine.

This distinction is what lets you encode a standard that has a strict core and a softer edge. In the example below, type is mandatory, while title and tags are validated when present but never demanded:

metadata.schema.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["type"],
"properties": {
"type": { "type": "string", "minLength": 1 },
"title": { "type": "string" },
"tags": {
"type": "array",
"items": { "type": "string" }
}
}
}

Requiring a field guarantees it exists; constraining it guarantees it’s usable. JSON Schema gives you the building blocks, and docmeta supports the standard format checks through ajv-formats. Two formats come up constantly in document metadata:

Use "format": "date-time" to require an ISO 8601 timestamp. This catches the classic fat-fingered date: 2026-13-01, June 5th, or a bare 2026-06-25 where a full timestamp was expected.

"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 datetime of last meaningful change."
}

Passes: 2026-06-25T14:30:00Z Fails: June 5th, 2026-13-01

Beyond formats, the everyday constraints carry most of the weight:

  • enum: restrict a field to a fixed set of values, ideal for a controlled type or status: "enum": ["guide", "reference", "tutorial"].
  • minLength: reject empty strings ("minLength": 1).
  • array + items: validate every element of a list, as with the tags example above.

By default, JSON Schema lets a document carry fields your schema never mentions. Whether to permit that is a deliberate choice, controlled by additionalProperties:

  • "additionalProperties": true (or omitting it): extra keys are tolerated. Contributors can add experimental or tool-specific frontmatter without tripping the check. This is the lenient default and a good fit for a young or shared standard.
  • "additionalProperties": false: every key must be declared in "properties". An unexpected key is a failure. Use this to catch typos like titel: or to keep frontmatter tightly governed.

The schema anchoring docmeta’s default set is Open Knowledge Format (OKF) v0.1, addressed as google:okf:0.1. (Run docmeta schemas for the full built-in list.) It’s a useful reference because it combines every technique on this page in a realistic standard, so reading it is the fastest way to see how the pieces fit together. When no other schema applies, docmeta falls back to a set rather than to OKF alone: google:okf:0.1 together with passo-uno:seven-action:1.0, which constrains an optional action field and requires nothing of its own.

src/schemas/okf/0.1.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "google:okf:0.1",
"title": "Open Knowledge Format (OKF) v0.1",
"description": "Frontmatter schema for OKF v0.1 concept files. OKF requires only `type`; all other fields are recommended, and unknown keys are explicitly tolerated.",
"type": "object",
"required": ["type"],
"additionalProperties": true,
"properties": {
"type": {
"type": "string",
"minLength": 1,
"description": "A short string identifying the kind of concept."
},
"title": {
"type": "string",
"description": "Human-readable display name."
},
"description": {
"type": "string",
"description": "Single-sentence summary used for indexing and previews."
},
"resource": {
"type": "string",
"format": "uri",
"description": "A URI uniquely identifying the underlying asset."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Short strings for categorization."
},
"timestamp": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 datetime of last meaningful change."
}
}
}

Notice the design choices OKF makes, each one a decision you’ll face in your own schema:

  • Exactly one required field (type). OKF demands the bare minimum, so adopting it rarely breaks existing files.
  • additionalProperties: true, stated explicitly. Unknown keys are welcome by design, because OKF is meant to be extended.
  • Recommended fields with real constraints. title, description, and tags are optional, but if present they’re checked. resource and timestamp carry uri and date-time formats.
  • A $id and human-readable title/description. These aren’t required for validation, but they document the schema for the humans who read it, and $id is what gives the built-in its google:okf:0.1 address. Setting a $id in a schema file of your own does not register a new built-in id — built-ins are bundled into docmeta — so you reference your own schema by its file path or URL, never by its $id. Give it an id of your own, or leave it out; see the warning below before reusing one.

Everything above assumes you’re defining the field set yourself. docmeta ships seven schemas you can adopt without authoring anything — the built-in schema registry lists all of them in one table, with what each constrains and which two are on by default.

If your site is built with Docusaurus, start with docusaurus:docs:3.10 and its blog and pages counterparts. They encode the front matter contract of Docusaurus 3.10 and require nothing, so switching one on cannot fail a page that was already building — it only catches a sidebar_position: "2" that your site would have rejected later. They claim none of the type or action keys below, so they stack with any of the vocabularies. They do share tags with OKF, which allows only strings where Docusaurus also allows tag objects — see the overlap note before pairing those two.

If what you need is a closed list of document types, three of the built-ins are vocabularies: diataxis:diataxis:1.0 requires type and constrains it to the four Diátaxis forms, tgdp:templates:1.0 requires type and constrains it to the 25 template slugs of The Good Docs Project, and passo-uno:seven-action:1.0 constrains action to the seven reader actions of the Seven-Action model. The first two both claim type, so they are alternatives; either one pairs with the third.

The two type vocabularies each demand the key as well as constraining it, so neither needs a partner for presence-checking — stack one with OKF for the other OKF fields. Both schemas apply in full, in either order:

Terminal window
docmeta validate docs/ -s diataxis:diataxis:1.0 -s google:okf:0.1

Seven-Action is the exception: it rules that an action, if present, is legitimate, without insisting on one. That is what makes it safe to layer onto what you already have. Every value of all three, which combinations compose, and the crosswalk from Seven-Action to Diátaxis are in the taxonomy schemas reference.

A schema only matters once documents are validated against it. The most direct way to connect the two is to let a document name its own schema with a $schema key in its frontmatter. docmeta reads that key and validates the file against whatever it points to.

Here is a document naming the built-in OKF schema, the schema-ref.md fixture:

schema-ref.md
---
$schema: google:okf:0.1
type: guide
title: Self-Describing Document
---
# Self-Describing Document
This file names its own schema via `$schema`.

The $schema value can be a built-in id (as here), a path to a .json file you wrote, or a URL. To point at a local schema file instead:

---
$schema: ./schemas/metadata.schema.json
type: guide
title: My page
---
  1. Write your schema as a .json file using the patterns above, keeping the "$schema" dialect line at the top.

  2. Add $schema: to a document’s frontmatter, pointing at your file (or a built-in id, or a URL).

  3. Validate it:

    Terminal window
    npx docmeta validate path/to/document.md

    docmeta resolves the document’s $schema, validates the frontmatter, and reports any missing or malformed fields.