Advanced Step Patterns
Advanced Step Patterns: Examples 5–10
This section covers type auto-detection, HTML component libraries, structured data, step ordering, inline processing, and per-page content resolution.
Example 5: Type Auto-Detection
Sometimes you need to mix different content types in one directory. The type = "auto" setting (which is the default) detects file types by extension:
[[step]]
emoji = "📄"
path = "pages/"
processing = "page"
[[step]]
emoji = "📦"
path = "mixed-content/"
processing = "include"
type = "auto" # Can be omitted — it's the default
With this configuration, mixed-content/ can contain:
banner.html→ Included as HTML (no escaping)article.md→ Converted to HTMLcode.txt→ HTML-escaped plain text
In your template:
<header>📦banner</header> <!-- HTML -->
<article>📦article</article> <!-- Markdown → HTML -->
<pre><code>📦code</code></pre> <!-- Plain text (escaped) -->
When to use auto:
- Mixed content directories
- When you’re not sure what types you’ll need
When to use explicit types:
- Strict validation (only accept
.mdfiles, for example) - Better error messages
- Clear intent in configuration
Try it:
cd examples/05-content-types
ssi deploy site/ output/
Example 6: HTML Component Libraries
Build reusable UI components with type = "html":
[[step]]
emoji = "📎"
path = "blocks/"
processing = "include"
type = "html"
Create components in blocks/:
<!-- blocks/alert-info.html -->
<div class="alert alert-info">
<strong>Info:</strong> 💬message
</div>
<!-- blocks/card-basic.html -->
<article class="card">
<h3>💬title</h3>
<p>💬content</p>
</article>
Use in templates:
<main>
📎alert-info
📎card-basic
📎card-basic <!-- Reuse as many times as needed -->
</main>
Benefits:
- Write once, use everywhere
- Consistent UI components
- Easy to update (change one file, affects all pages)
- Combine with variables for dynamic content
Try it:
cd examples/06-html-blocks
ssi deploy site/ output/
Example 7: Structured Data with TOML
Store site-wide variables and configuration in TOML files:
[[step]]
emoji = "📊"
path = "data.toml"
processing = "include"
type = "plain"
options = ["inline"]
Your data file:
[_]
site_name = "My Site"
author = "Jane Doe"
version = "2.1.0"
contact_email = "jane@example.com"
In templates:
<header>
<h1>📊site_name</h1>
<p>By 📊author • Version 📊version</p>
</header>
<footer>
<p>Contact: 📊contact_email</p>
</footer>
Benefits:
- Centralized configuration
- Easy for non-technical users to edit
- No HTML knowledge required for content updates
- Perfect for site-wide variables
Try it:
cd examples/07-toml-data
ssi deploy site/ output/
# Edit site/data.toml and regenerate to see changes
Example 8: Step Ordering
SSI processes steps in the order they appear in ssi-config.toml. Each step scans the full accumulated page text for its tokens and replaces them. A step has no knowledge of which earlier step introduced a token — it only sees whether the token is present in the text when it runs.
The two ordering rules
Rule 1: The page step must come first. Include steps that appear before the page step are invisible to the pipeline — their tokens remain as literal text in the output.
Rule 2: Among include steps, dependency order matters. If a block file contains a 💬 token, the HTML include step for that block must run before the plain-text include step that resolves 💬 tokens. The plain-text step scans whatever text is present when it runs — so the blocks must already be expanded.
[[step]]
emoji = "📄"
path = "pages/"
processing = "page"
[[step]]
emoji = "📦" # HTML include step — runs first among includes;
path = "blocks/" # block files may themselves contain 💬 tokens
processing = "include"
type = "html"
[[step]]
emoji = "💬" # plain-text include step — runs after HTML step;
path = "strings.toml" # resolves all 💬 tokens, including those from expanded blocks
processing = "include"
type = "plain"
options = ["inline"]
Origin is irrelevant
Once a step places content into the accumulated page text, later steps cannot tell where it came from. A 💬 token that arrived from an HTML include, a Markdown include, a TOML value, or was typed directly in the page template is treated identically by the next step. This is also why step ordering can be used to construct new token keys on the fly — see Example 24 (combining-tokens) for a demonstration of that pattern.
leftovers-okay for documentation content
Use options = ["leftovers-okay"] on include steps whose content contains token-like strings that should remain as literal text — for example, a step that includes HTML files showing SSI configuration syntax as code examples.
[[step]]
emoji = "📁"
path = "code-examples/"
processing = "include"
type = "html"
options = ["leftovers-okay"] # code comments contain 📦 and 💬 as display text
Try it:
cd examples/08-step-order
ssi deploy site/ output/
Example 9: Inline vs Block Processing
Control how content is formatted with options = ["inline"]:
# Inline: newlines become spaces
[[step]]
emoji = "📝"
path = "inline-text.toml"
processing = "include"
type = "plain"
options = ["inline"]
# Block: preserves formatting
[[step]]
emoji = "📃"
path = "block-text.toml"
processing = "include"
type = "plain"
Inline behavior:
<p>This is 📝short-phrase in the middle of a sentence.</p>
<!-- Result: "This is short text phrase in the middle of a sentence." -->
Block behavior:
<figure>
📃code-example
</figure>
<!-- Result: Preserves all line breaks and indentation -->
When to use inline:
- Variables in sentences
- Short text snippets
- Navigation links
- Metadata
When to use block:
- Paragraphs
- Code examples
- Multi-line content
Try it:
cd examples/09-inline-content
ssi deploy site/ output/
Example 10: Per-Page Content Resolution
The same token can resolve to different content depending on which page is being processed.
[[step]]
emoji = "📝"
path = "markdown-texts/"
processing = "include"
type = "markdown"
Directory structure:
markdown-texts/
welcome.md # Default for all pages
index/
welcome.md # Specific to index.html
about/
welcome.md # Specific to about.html
example/
welcome.md # Specific to example.html
All three pages use the same template:
<section class="welcome">
📝welcome
</section>
But each gets different content:
index.html→ Usesmarkdown-texts/index/welcome.mdabout.html→ Usesmarkdown-texts/about/welcome.mdexample.html→ Usesmarkdown-texts/example/welcome.md
Resolution order:
- Check page-specific subdirectory (
markdown-texts/index/) - Fall back to top-level (
markdown-texts/)
Benefits:
- Same structure, different content
- No configuration changes to add pages
- Easy to override specific pages
- Shared defaults with per-page customization
Try it:
cd examples/10-page-specific
ssi deploy site/ output/
# Compare index.html, about.html, and example.html
Pattern Summary
| Example | Pattern | Best For |
|---|---|---|
| 5 | Type auto-detection | Mixed content directories |
| 6 | HTML components | Reusable UI widgets |
| 7 | TOML data | Site-wide variables |
| 8 | Step ordering | Configuring the pipeline correctly |
| 9 | Inline processing | Mid-sentence variables |
| 10 | Per-page resolution | Page-specific content |
Combining Patterns
These patterns work well together:
# Pages
[[step]]
emoji = "📄"
path = "pages/"
processing = "page"
# Reusable components
[[step]]
emoji = "📎"
path = "blocks/"
processing = "include"
type = "html"
# Site-wide variables
[[step]]
emoji = "💬"
path = "strings.toml"
processing = "include"
type = "plain"
options = ["inline"]
# Per-page content
[[step]]
emoji = "📝"
path = "markdown-texts/"
processing = "include"
type = "markdown"
This gives you reusable HTML components (📎), site-wide variables (💬), and per-page content (📝) in one site.
Processing Order Strategy
Understanding how SSI processes include sources in order helps you make strategic decisions about configuration.
How Sequential Processing Works
Include sources process sequentially, with each source scanning the output from all previous sources:
Template: <div>📎widget 💬title</div>
After source 1 (📎): <div><h1>Header</h1> 💬title</div>
After source 2 (💬): <div><h1>Header</h1> My Site</div>
Each step scans the accumulated result, not just the original template.
Forward-Only Processing
Because processing moves forward through sources:
- Earlier sources can use tokens from later sources (they haven’t been expanded yet)
- Later sources scan content inserted by earlier sources (it’s already in the template)
- The last source’s content is not scanned again (no more steps)
Strategic Ordering: Preventing Token Expansion
Place sources containing token-like patterns last in your configuration to prevent unwanted expansion.
Example: Including third-party HTML that happens to contain emoji followed by text:
[[step]]
emoji = "📎"
path = "blocks/"
processing = "include"
type = "html"
[[step]]
emoji = "💬"
path = "config.toml"
processing = "include"
type = "plain"
[[step]] # Place last — content won't be scanned for 📎 or 💬
emoji = "🎨"
path = "third-party-widgets/"
processing = "include"
type = "html"
If a file in third-party-widgets/ contains 📎something in its HTML:
- Placed first: Would be expanded if
blocks/contains a matching file - Placed last: Stays as literal text
📎something
Other uses for last-position sources:
- User-generated content you don’t control
- Code examples showing SSI syntax
- Raw HTML from external sources
Dependency Ordering
For sources that should interact, order them by dependency:
[[step]] # Base layer — defines fundamental variables
emoji = "🔧"
path = "config.toml"
[[step]] # Middle layer — can use 🔧 tokens
emoji = "📎"
path = "blocks/"
[[step]] # Top layer — can use 🔧 and 📎 tokens
emoji = "📝"
path = "content/"
This creates a natural hierarchy: config → components → content.