Content Overview
Content in Alloy lives in the content/ directory. Each file with a supported extension (.md, .html by default) becomes a page on your site. Alloy reads the front matter, applies the data cascade, renders the body, wraps it in a layout, and writes the result to _site/.
Supported content formats
The content.formats config controls which file extensions are treated as content. The default is ["md", "html"]:
Files matching these extensions go through the full pipeline: front matter extraction, data cascade, template rendering, and layout wrapping. Everything else in content/ is treated as a passthrough file and copied to the output as-is.
Front matter is required
Every content file must start with front matter delimiters. Alloy supports YAML (---), TOML (+++), and JSON ({}):
Empty front matter is valid – use --- followed by --- on the next line when you have no metadata to set. Files without any front matter delimiters produce a build error.
How content is processed
Every content file passes through a multi-stage pipeline:
- Discovery – Alloy walks the
content/directory and identifies content files by extension. - Front matter extraction – Metadata is parsed from the top of each file.
- Data cascade – Global data, directory data (
_data.yaml), and front matter are merged. See Data Cascade. - Permalink resolution – The output URL is computed from front matter, cascade patterns, or the file path. See Permalinks.
- Content rendering – Markdown is converted to HTML. Template tags (
{{ }},{% %}) are evaluated. - Layout wrapping – The rendered content is injected into a layout template as
{{ content }}. - Output – The final HTML is written to
_site/.
For a deeper look at each stage, see Content Lifecycle.
Markdown configuration
Alloy uses goldmark for Markdown rendering. Configure it in alloy.config.yaml:
With customElements: true (the default), a block-level custom element (any tag containing a dash, like <my-gallery>) is kept intact through Markdown rendering – including blank lines and nested elements inside it – instead of being split at the first blank line as standard HTML blocks are.
Template tags in inline code and fenced code blocks always display as typed – the templateTags setting only affects prose. Set templateTags: false to also show {{ }} and {% %} in prose as literal text instead of evaluating them, for sites that write about template syntax rather than with it. For a one-off literal tag, wrap it in {% raw %} instead. See Literal template syntax for the full rules.
Block-level attributes
When autoHeadingID is true (the default), {.class #id key=value} attribute syntax is active on block elements: headings, fenced code blocks, blockquotes, and tables. See Render Hooks — Block-level attributes for the full syntax and supported elements.
Table of contents
Alloy extracts the heading structure from each Markdown page during rendering and exposes it as page.toc:
Each TOC entry has:
| Field | Type | Description |
|---|---|---|
id |
string | The heading’s id attribute (auto-generated or {#custom-id} override) |
text |
string | Plain text content of the heading |
level |
int | Heading level (2–6; h1 is excluded) |
children |
array | Nested headings one level deeper |
TOC extraction is controlled by content.markdown.toc (default: true). Set toc: false to disable it – page.toc will be nil for all pages. This is independent of content.markdown.goldmark.autoHeadingID, which controls whether headings get id attributes in the HTML output. Disabling TOC does not disable heading IDs – anchors still work, you just don’t get the structured data.
For non-markdown content (.html, .liquid), page.toc is always empty. Plugins can build TOC for those pages via the onContentTransformed hook.
HTML content files
.html files in content/ are classified based on their content:
- Has front matter – Processed as a content page, same as Markdown.
- No front matter + starts with
<!DOCTYPEor<html>– Treated as a full HTML document and copied to output as-is (passthrough). - No front matter + no DOCTYPE – Treated as an HTML fragment. The file body becomes the page content with empty front matter, inheriting its layout from the
_data.yamlcascade.
This classification lets you co-locate standalone HTML documents alongside templated content without adding front matter to every file.
Co-located assets
Non-content files inside content/ are automatically copied to the output, preserving their relative path. This enables page bundles where a post and its images live together:
Reference co-located assets with relative paths in your Markdown: .
Custom directory structure
Override the default content/ path in config:
All pipeline stages, the file watcher, and the dev server respect the configured path.
What to read next
- Front Matter – All available front matter fields
- Data Cascade – How global, directory, and page data merge
- Permalinks – URL patterns and tokens
- Pagination – List pages and virtual page generation
- Content Lifecycle – Draft, future, and expired content
- Data Files – Global data from
data/and external sources