Skip to content
Get started

zel check

Validate a content folder against the schemas the build uses.

terminal
zel check
zel check ./docs
✓ ./content
  site        Acme · theme cupertino
  pages       6
  guides      1 (5 pages) · /guide
  menu        Features · Pricing · Solutions · Resources
  home        /
  routes      11

And when something is wrong:

✗ ./content

  pages.yaml:14
    section "sections/home/pricing.yaml" does not exist

  01-guide/_sidebar.yaml:9
    "installation" is not a page in this guide
    did you mean "install"?

  01-guide/quickstart.mdx:31
    link to /guide/themes/nord — no such page

3 problems

The one thing worth understanding

check runs the engine's own schemas, not a reimplementation of them.

That is why it needs a container, and it is the reason to trust it. The alternative — parsing YAML in Rust and checking it against rules written a second time — would produce a validator that agrees with the build on the day it is written and disagrees with it a month later. Every field added to a section type would need adding twice, and the failure mode is the worst kind: check passes, the build fails, and the two disagree about the same file.

So the CLI does not know what a hero is. It mounts your folder read-only, starts the engine, and asks.

The cost is a container runtime. The benefit is that anything check accepts, the build accepts — by construction, not by diligence.

What it validates

Every YAML fileAgainst its schema. Unknown values fail, naming what is accepted
Every sectionThe type exists, required fields are present, values are in range
Every internal linkIn menu.yaml, site.yaml's footer, pages.yaml, and inside MDX prose
Every sidebar entryNames a page that exists in that guide
Every asset referenceResolves under assets/
The home pagehome: names a page that exists

Errors against warnings

An unknown top-level key warns. A content folder written for a newer Zellij still builds on an older one, which is what lets a team upgrade the engine and the content separately.

An unknown value for a known key fails, naming the key and listing what it accepts. theme: cupertno is a typo, not forward compatibility, and treating it as one would render the wrong theme silently.

What it does not do

It does not compile your MDX.

check validates the content model. Guide pages are compiled as MDX — Markdown plus JSX — and that is a separate pass with its own failure modes. { opens an expression and < opens a tag, so:

set {timeout} to <n> seconds

is good Markdown, valid as far as check is concerned, and a syntax error when the site builds.

This matters most after a conversion, where prose written for a tool that never had to care meets a compiler that does. Build the folder once before you throw the original away:

terminal
zel build ./content

Where it belongs

As a pre-commit hook. It exits non-zero, takes a second, and catches the class of mistake that is invisible in a diff — a renamed page that three sidebars still point at.

As the first job in a pipeline. zel ci init writes exactly that: a check on every pull request, needing no credentials and no registry. A link to a page somebody deleted gets caught before review rather than after deploy.

Before every build. zel build, zel dev and zel deploy all run it first, so a broken link fails in a second rather than after a four-minute compile.

When the folder is somewhere the runtime cannot see

error: my-docs is not shared with the container runtime

A bind mount of an unreachable path does not fail — it mounts an empty directory, and the checker faithfully reports a site with no pages. True of what it was shown, and a lie about what you asked.

Colima shares $HOME and not /tmp:

terminal
colima stop && colima start --mount '/tmp:w'

Options

[dir]The content folderResolved

The intended --json

json
{
  "ok": false,
  "root": "./content",
  "problems": [
    {
      "file": "pages.yaml",
      "line": 14,
      "severity": "error",
      "message": "section \"sections/home/pricing.yaml\" does not exist"
    }
  ]
}

The intent is that every command supports --json, because a tool only a human can read cannot be used by a pipeline. None of them do yet.

See also

  • zel dev — the same validation, plus a browser
  • zel fmt — formatting, which is a different question
  • zel ci — running this on every pull request
  • The wrapper appnpx zellij check, if you keep one