Snippets & includes#
Some prose belongs on many pages: an installation warning, a prerequisites block, a support notice. Write it once as a snippet and include it wherever it's needed — when it changes, every page picks up the new text on the next build.
Write a snippet#
A snippet is a plain Markdown file. By convention it lives in _snippets/ —
any folder starting with _ is already excluded from pages and navigation, so
snippets never become pages themselves:
content/
_snippets/
requirements.md
guides/
install.md
<!-- content/_snippets/requirements.md -->
:::note
Ovellum needs **Node 20+**. Check with `node --version`.
:::
Snippets can use everything a page can — component directives, code blocks, images — because they're spliced in before rendering: the included content flows through the same pipeline (and the same HTML sanitizer) as the page's own Markdown.
Include it#
Use the ::include directive (note the two colons — it's a leaf
directive, on its own line):
# Install
::include[/_snippets/requirements.md]
Now run the installer…
- A root-absolute path (
/_snippets/…) resolves from the content root — the same convention as asset references. - A relative path (
::include[warning.md]) resolves against the including file's folder. - The attribute form
::include{file="/_snippets/requirements.md"}is equivalent.
Snippets can include other snippets (nesting is fine; circular includes are caught and skipped with a warning). A snippet's own frontmatter is metadata about the snippet — it's stripped, never merged into the page. Headings inside a snippet join the page's table of contents like any other heading.
With multiple languages#
On an i18n site, an include resolves in the current
locale's tree first, then falls back to the default locale's tree. So
you can translate a snippet by creating the same path under content/<code>/
— locales that haven't translated it yet render the default-locale version
instead of a hole.
When something is wrong#
A missing target, a circular include, or a path that tries to escape the content directory never breaks the build and never leaks raw markup: the directive is omitted and the build reports a warning naming the file and line.
ovellum check validates every include target too — a broken include is a
normal issue (exit 1), so CI catches it before readers do:
ovellum check complete in 12ms
...
broken includes: 1
details:
guides/install.md:5 broken include /_snippets/requirments.md — file not found
Boundaries (by design)#
- Manual mode only — includes are a site-builder feature; auto/hybrid generated Markdown is produced from source, not composed from snippets.
- The
.mdmirrors show the authored source — like component directives, an::includeline appears as written in a page's AI mirror, not expanded. - Includes stay inside the content directory — no
..escapes, no absolute filesystem paths, same containment rule as everything else.