Defaults and Fallbacks
Defaults and Fallbacks
SSI resolves include tokens by looking for page-specific content first, then falling back to shared defaults. This single rule applies uniformly across all include source types. Understanding it lets you write templates that say “use this page’s version, or the default if none exists” — without any conditional logic.
Resolution Order
For every token SSI encounters in a template, it checks two places in order:
- Page-specific: content named for the current page
- Default: content at the top level
If a page-specific version exists, it is used exclusively. There is no merging — the page-specific version replaces the default entirely.
Directory Sources
For a directory include source, SSI looks for a subdirectory matching the page name:
content/
├── description.md ← default (any page without its own)
├── index/
│ └── description.md ← used only when processing index.html
└── about/
└── description.md ← used only when processing about.html
When processing about.html, the token 📝description resolves to
content/about/description.md. When processing contact.html, no
page-specific version exists, so it resolves to content/description.md.
TOML Sources
For a TOML include source, SSI uses section headers as the page selector:
# strings.toml
[_]
title = "My Site" ← default for any page
[about]
title = "About Us" ← used only when processing about.html
[contact]
title = "Contact" ← used only when processing contact.html
The [_] section is the default. A section named [pagename] (matching the
filename without .html) overrides the default for that page.
Template Example
A single template works for all pages:
<head>
<title>💬title — 💬site_name</title>
<meta name="description" content="💬description">
</head>
With the TOML above:
| Page | 💬title resolves to |
|---|---|
index.html | "My Site" (default) |
about.html | "About Us" (page override) |
contact.html | "Contact" (page override) |
blog.html | "My Site" (default — no [blog] section) |
The Defaults-to-Empty Pattern
Setting a TOML key to an empty string in [_] creates a token that produces
nothing unless a page explicitly sets it. This is the idiomatic SSI way to handle
optional content.
[_]
hero_class = "" ← empty by default
[home]
hero_class = "hero--featured"
[about]
hero_class = "hero--with-image"
In the template:
<section class="hero 💬hero_class">
📎hero-content
</section>
For index.html and pages without an override, the extra class is simply absent —
the token resolves to an empty string. No conditionals needed.
This pattern also works for optional attributes:
[_]
page_class = ""
[docs]
page_class = "layout--wide"
<body class="page 💬page_class">
Fallback Field
Include steps also support an explicit fallback field — an alternative source path
used when the primary source has no match for a token:
[[step]]
emoji = "📝"
path = "content/"
processing = "include"
type = "markdown"
fallback = "shared-content/"
If content/ (and content/pagename/) have no file matching a token, SSI checks
shared-content/ before leaving the token unresolved.
No Partial Merging
Page-specific content always takes complete precedence. There is no “merge with
default” behavior. If strings.toml has:
[_]
title = "My Site"
description = "Widgets for everyone"
[about]
title = "About Us"
Then for about.html:
💬title→"About Us"(page override)💬description→"Widgets for everyone"(falls back to[_], no[about]entry)
The [about] section does not need to duplicate keys it doesn’t override.