Skip to content
Quarryv1.4

    Start free

    Content

    Theme documentation

    Five collections, all defined and validated in src/content.config.ts. Three are folders of Markdown, two are JSON files. There is no CMS and no database: the site is these files, built to HTML.

    Every page reads its collection through src/lib/content.ts rather than calling getCollection itself, which is where the draft filter, the sort orders and the date format live. If you need a new list of something, add the function there.

    docs

    src/content/docs/<slug>.md or .mdx. The filename is the URL: install.mdx is /docs/install.

    Field Type Required Meaning
    title string yes The page heading, the sidebar row and the pager label
    description string no The lead under the heading, the meta description, and the line under the title on /docs
    group string yes Sidebar group. One of docs.groups in src/config.ts; anything else sorts to the end
    order integer > 0 yes Position inside the group. The previous/next pager follows it
    lastUpdated date no Printed at the foot of the page. Absent: no date
    draft boolean no true keeps the page out of the build, in dev as well
    ---
    title: Install
    description: Install Quarry on macOS or Linux, then confirm the CLI and daemon are on your path.
    group: Getting started
    order: 2
    lastUpdated: 2026-06-10
    ---

    Adding a page is adding a file: the sidebar, the group number, the breadcrumb, the pager and the search index all follow from the collection.

    changelog

    src/content/changelog/<file>.mdx, one file per release. The filename is not the URL — every release is printed on the one /changelog page, newest first — so it needs no zero padding.

    Field Type Required Meaning
    version string yes Sorted with numeric collation, so 1.10.0 lands above 1.4.0
    date date yes Printed in the rail beside the version
    summary string no The release list and the feed. Plain text, not Markdown
    breaking boolean no Marks the release in the rail instead of filing it apart

    The body is the release note itself, written under ### headings (Added, Fixed, and so on). The version chip beside the wordmark in the header is read from the newest entry here, which is why there is no version field in the config to forget.

    plans

    src/content/plans.json, an array. Each object is a column on /pricing and a block on the home page.

    Field Type Required Meaning
    id string yes The key. Not printed
    name string yes Plan name
    tagline string yes One line under the name
    priceMonthly number yes Per month, in currency
    priceYearly number yes Per year. The saving against twelve months is computed
    currency string no ISO code, USD by default. Formatted with Intl for site.locale
    features string[] yes Printed in the plan column. At least one
    matrix object no Rows of the comparison table: label → true, false or a string
    cta { label, href } yes The button
    featured boolean no Prints “Most bought” over the column and makes its button the solid one. Set it on one plan
    order integer > 0 yes Left to right

    matrix keys are shared across plans on purpose: the table prints the union of them, in the order the plans list them. A plan that does not mention a row prints an em dash; false prints No.

    blog

    src/content/blog/<slug>.md or .mdx. The filename is the URL: /blog/<slug>.

    Field Type Required Meaning
    title string yes
    description string yes The row on the index, the meta description, the feed
    pubDate date yes Sort order, newest first
    updatedDate date no A second line under the byline
    tags string[] no Each tag gets a page at /blog/tag/<tag>; the slug is generated
    author string no The name as it should be printed. Falls back to site.author in the JSON-LD
    series string no Posts carrying the same string are one series
    draft boolean no true keeps the post out of the build, in dev as well

    author is a plain string rather than a reference, so a name with no entry in authors.json still prints its byline. A series has no part numbers: the parts are ordered by publication date, which cannot disagree with itself the way two hand-typed numbers can. A draft part is not in the list, so a series that announces two parts delivers two.

    authors

    src/content/authors.json, keyed by a slug of your choosing. The match with a post is on name, not on the key.

    Field Type Required Meaning
    name string yes Exactly as the post’s author writes it
    role string yes Printed under the post, beside the name
    url string no Absent: the name is set, not linked

    Add a person by adding an entry. Remove one and the byline keeps printing the name; the block at the foot of the post is what disappears.

    What is computed, and therefore not a field

    Reading time (from the body), the group number in the sidebar, the yearly saving, the previous and next documentation page, the parts of a series and their numbering, the related posts, the tag counts and every URL. Do not add a field for any of them.

    Math

    $…$ sets a formula inline and $$…$$ on its own lines, in posts, docs pages and changelog entries, .md and .mdx alike. KaTeX turns it into HTML at build time, so the page ships no script for it, and only a page with math loads the KaTeX stylesheet. Chemical equations use mhchem: $\ce{2H2 + O2 -> 2H2O}$.

    Two dollar signs in one paragraph ($20 and $30) read as a formula between them. Write \$ for a literal dollar sign.

    In Pro, \label{eq:name} in a $$…$$ formula numbers it, and $\eqref{eq:name}$ anywhere on the page prints (1) as a link to it ($\ref{eq:name}$ prints the bare number). A reference may come before its equation. Copying a formula copies its LaTeX source.

    For a statement and its proof, import Theorem in an .mdx file: <Theorem kind="definition" title="Optional name">…</Theorem>. kind is theorem, lemma, proposition, corollary, definition, example, remark or proof. The first six share one count per page; a proof ends with ∎. The words are ui.statements in src/config.ts.

    MDX

    A .md file is prose. Use .mdx when the page needs a callout, a set of language tabs or a parameter table; the components are imported at the top of the file, and customization.md lists them with their props.

    Two things to know before you write one:

    • pnpm format skips src/content/**/*.mdx. Prettier’s Markdown printer reflows a fenced block that sits inside a JSX element, which turns a tab full of shell into one line of it.
    • A fence inside <Fragment slot="…"> keeps its indentation. Indent it with the element; it reads better and nothing breaks.

    Adding a field

    Edit the zod schema in src/content.config.ts. A new field must be .optional() or carry a .default(), or every existing file in that collection fails validation until it is edited. pnpm check is what tells you.

    Next: Customization