Falz Docs

Layout reference

Layouts live under .falz/schemes/. The configured layout pattern is layout.xml by default.

<Layout>

The root element needs exactly one useful template source:

<Layout src="page.html">...</Layout>

or a simple inline template:

<Layout tpl="{{body}}">...</Layout>

Use tpl for short wrappers such as a single placeholder. Use src for HTML or other markup so the template remains readable and does not need XML attribute escaping.

Attribute Meaning
src Scheme template file. Falz searches from the current source directory upward through the scheme tree.
tpl Inline template text. XML-sensitive characters must be escaped.

If both are present, tpl wins. External templates are easier to read and maintain.

Template syntax

Placeholders use {{name}} by default. Values may come from content, fragments, <Var> elements, or TransformResult.vars. The opening and closing delimiters are set by template_config.

Conditional blocks use if, optional else, and end:

{{if description}}
  <meta name="description" content="{{description}}">
{{else}}
  <meta name="description" content="Falz documentation">
{{end}}

Conditions support:

  • nested conditional blocks;
  • ==, !=, <, >, <=, and >= comparisons;
  • !, &&, and || operators;
  • parentheses;
  • variable names, quoted strings, booleans, and decimal numbers.

Missing values, empty strings, empty byte slices, and boolean false are false. All other values are true. Comparisons do not convert between types.

<Content>

A content rule matches files in the source tree:

<Content name="body" src="*.md" pipe="md" to="{FILENAME}.html">
  <Var name="section" value="Docs"/>
  <Asset name="images" src="*.png" to="images/{BASENAME}"/>
</Content>
Attribute Required Meaning
name yes Layout slot to fill, for example {{body}}. During this entry's custom index() and transform() calls, the same value is available as this.entry.tag.
src yes Source pattern. It may not contain / or \\.
pipe no Pipe name. Omit it to copy the source bytes unchanged.
to yes Output path template, relative to the matched source directory.

Each match becomes a separate output page. A template with multiple content slots does not combine independently matched source files into one page; each content entry fills its own slot when its page is rendered.

<Fragment>

A fragment renders into a layout slot. It can read a scheme file, or call a pipe without a source file:

<Fragment name="nav" src="nav.md" pipe="md"/>
<Fragment name="footer" pipe="shell"/>
Attribute Required Meaning
name yes Template slot, such as {{nav}}.
src no Scheme file or glob searched upward from the layout context. Required when pipe is omitted.
pipe no Pipe used to render the fragment; required when src is omitted. Without a pipe, source bytes pass through.

The pipe receives the Fragment's layout name as this.name and this.entry.tag, with this.entry.type set to "fragment". Like normal pipes, it receives project variables in this.globals, Content <Var> values in this.vars, and timing methods in this.debug. Content variables are also available as top-level properties for compatibility, but cannot overwrite context fields. this.file is present only when src is supplied, with paths relative to the scheme root. With no source, the transform receives an empty input buffer; no selector file is needed. The generated fCtx type describes the fragment context.

If a pattern finds several files while walking upward, Falz uses the first result, which is the nearest match. Fragment source files (when present) and fragment-pipe bundles are layout dependencies, so changing either rebuilds dependent content. A source-less fragment retains its pipe dependency.

<Asset>

A top-level asset reads from the scheme tree:

<Asset name="css" src="*.css" pipe="minify-css" to="assets/{BASENAME}"/>

Top-level assets are scanned once per layout, from the first layout context found while walking the source tree. Unlike <Content>, they are not evaluated once in every source directory, and their src pattern is not recursive. Since * does not match /, include subdirectories explicitly:

<Asset name="js" src="js/*.js" to="{FILENAME}.js"/>

For example, .falz/schemes/js/app.js matches this rule and is copied unchanged to dist/js/app.js (with the default output directory). {FILENAME} expands to app, and the asset's scheme-relative directory, js/, remains the output base. The to path is appended to that base; it does not move the file to the output root.

An asset nested inside <Content> reads from the source tree:

<Content name="body" src="*.md" pipe="md" to="{FILENAME}.html">
  <Asset name="images" src="*.png" to="images/{BASENAME}"/>
</Content>
Attribute Required Meaning
name yes Entry name used internally and as this.entry.tag. It must be unique within the layout's output rules.
src yes Source pattern.
pipe no Optional transform; omitted means pass-through.
to yes Output path template.

Assets are never wrapped in the layout template.

<Var>

<Var> is allowed inside <Content>:

<Var name="section" value="Guides"/>

name is required. value is a string. The value is available:

  • as {{section}} in the layout template;
  • as this.vars.section in that content pipe;
  • while rendering fragments for that content layout instance.

Output path placeholders

The to attribute uses single braces:

Placeholder Value for posts/hello.md
{FILENAME} hello
{EXT} md
{BASENAME} hello.md
{PIPE} selected pipe name
{ALIAS} alias returned by a custom pipe, or empty
{HASH} full combined entry hash
{HASH:N} first N hash characters, for example {HASH:8}

The rendered path is relative to the entry's base directory. For a content file in posts/, to="{FILENAME}.html" writes posts/hello.html. Directory segments are allowed: to="{FILENAME}/index.html" writes posts/hello/index.html, creating directories as needed.

Use {ALIAS} when the pipe calculates a routing name from source data. This pipe result sets the alias to hello-world:

return { content: html, alias: "hello-world" }

A content rule can place that value anywhere in its output pattern:

<Content name="body" src="*.md" pipe="page" to="articles/{ALIAS}/index.html"/>

For a root source file, the resulting path is articles/hello-world/index.html. An alias is usually a slug, locale-aware name, external ID, or other value that cannot be derived from the source filename alone.

Unknown output placeholders are emitted as an [unknown tag ...] marker. Treat that as a configuration error.

Source patterns

A src value is a space-separated list of include and exclude tokens:

  • *.json
  • *.{json,yaml} — one level of alternatives;
  • *.md !draft.md !private-*.md — exclusions match the basename.
Pattern Matches in each governed source directory
*.md Every filename ending in .md.
page?.md page1.md or pagea.md; ? matches one character.
[ab]*.md Markdown filenames beginning with a or b.
*.{md,markdown} Either extension.
*.md *.txt The union of both include patterns.
*.md !draft.md Markdown except draft.md.

Exclusions match the basename and only remove included files. Brace expansion supports one {a,b} group, not nested groups.

Patterns follow slash-separated Go path.Match syntax. * and ? do not cross /. Falz applies content rules separately in every source directory governed by the layout, so recursive ** semantics are unnecessary.

Top-level scheme assets are scanned once per layout, not once per source directory. Their patterns are non-recursive: use src="js/*.js" for files under a scheme's js/ directory. Nested assets are scanned with their content entry's source directory.

Layout resolution

For a source directory a/b, Falz looks for the layout pattern in:

  1. .falz/schemes/a/b/;
  2. .falz/schemes/a/;
  3. .falz/schemes/.

It stops at the first level with matches. If the layout setting is a glob and several layouts match at that level, all are applied.

Templates and fragments use upward scheme lookup too. A root nav.md can serve posts unless a nearer scheme directory supplies its own version.

See Layouts for an example.