Schemas
A schema says what the entries of a section may contain: which fields, of which types, which of them every entry needs. The five built-in sections have theirs in vitakit. A section of your own has one in schemas/<section>.ts, a small TypeScript file — usually written for you by vitakit add section.
A schema decides what is accepted, not what is shown. Showing a field is a separate step, on the website and in the PDF: see customize.
A section of your own
Write your entries first, in content/<name>.yml, then let the command draft the schema from them:
pnpm vitakit add section outreachnpx vitakit add section outreach# content/outreach.yml
- date: 2025-03-14
title: What is a random tree?
venue: BRG Klagenfurt-Viktring
audience: school
- date: 2024-11-02
title: Maths night
audience: public// schemas/outreach.ts, as drafted
import { section, dateRange, richText, text } from '@anja.codes/vitakit'
export default section({
date: dateRange,
// Markdown and $math$ allowed
title: richText,
venue: text.optional(),
audience: text,
})The draft looks at every value of every entry:
- a field missing from some entries is
.optional(); dateis a date, a field calledtitleis formatted text,{ name, url }values are links, web addresses are web addresses, numbers stay numbers;- what could reasonably be different is a comment above the field, not a decision: a few repeated values that could be a fixed list, a date written in a form vitakit does not read (
03/2015— write2015-03), a field name that needs quotes; - an entry the draft does not accept — an empty value, a number in one entry and text in another — is listed, with what to change, before anything is written.
Check the comments, change what is not right, and delete them. The file is yours: edit it freely afterwards.
Writing one by hand
import { section, dateRange, richText, linked, z } from '@anja.codes/vitakit'
export default section({
date: dateRange,
title: richText, // Markdown and $math$
venue: linked.optional(),
audience: z.enum(['school', 'public', 'press']),
participants: z.number().optional(),
})section() makes a list of entries, each of which also accepts visibility. Use the vitakit building blocks — dateRange, richText, text, url, linked — for dates, formatted text, plain text, web addresses and links, and your section sorts, filters and formats like a built-in one. Anything else Zod can express works too: z is exported for that.
Every entry is strict: a field the schema does not name is an error, so a typo cannot silently drop a line from your CV.
More fields for a built-in section
pnpm vitakit add field teaching evaluationScore --type numbernpx vitakit add field teaching evaluationScore --type numberThe first field creates schemas/teaching.ts, which keeps vitakit's fields and adds yours; later fields are added to the same file. It is also easy to write:
import { schemas, extendSection, z } from '@anja.codes/vitakit'
export default extendSection(schemas.teaching, {
evaluationScore: z.number().optional(),
})A field listed there that vitakit already has replaces vitakit's rule for it. If your entries already have the extra fields, vitakit add section teaching drafts all of them at once.
To change or remove built-in rules wholesale, take over the whole definition — the file vitakit itself uses, not a paraphrase:
pnpm vitakit customize teaching --schema --copy # -> schemas/teaching.tsnpx vitakit customize teaching --schema --copy # -> schemas/teaching.tsA copied schema no longer gets vitakit's improvements to it; an extended one does.
One schema for two sections
Two sections with the same fields — say, scientific and public talks in two files — can share a schema:
pnpm vitakit add section talks-public --like talksnpx vitakit add section talks-public --like talks// schemas/talks-public.ts
export { default } from './talks-sci.ts' // another section's schema// schemas/talks-public.ts
import { schemas } from '@anja.codes/vitakit'
export default schemas.talks // a built-in section's schemaA relative import needs its .ts extension: schemas are loaded by Node itself. Often a single section with a field to tell entries apart is simpler — one talks.yml with kind: outreach — and where splits it on the page.
Which schema is used
For content/<name>.yml, the first of:
schemas/<name>.tsin your project,- vitakit's built-in schema of that name,
- none: the section is reported, and the build fails, until it has one.
vitakit explain <section> shows which one is in use and what fields it accepts.