Custom HTML and Template Files
Statik supports multiple file formats for your content, giving you flexibility in how you author your pages and posts.
Supported File Types
Markdown Files (.md)
The standard format for content. Markdown is processed and converted to HTML.
---
title: My Blog Post
published: 2024-10-07T10:00:00
---
# Hello World
This is a **markdown** post.
HTML Files (.html)
Pure HTML files with optional frontmatter. The HTML content is used as-is without any Markdown processing.
posts/custom-post.html
---
title: Custom HTML Post
published: 2024-10-07T10:00:00
description: A post with custom HTML
layout: default
---
<div class="custom-container">
<h1>Custom HTML Content</h1>
<p class="highlight">This HTML is used directly without Markdown processing.</p>
<ul class="custom-list">
<li>Full control over markup</li>
<li>Custom CSS classes</li>
<li>Complex layouts</li>
</ul>
</div>
Handlebars Template Files (.hbs)
Dynamic template files that can use Handlebars expressions and have access to all template variables.
pages/dynamic-page.hbs
---
title: Dynamic Page
nav_order: 1
custom_var: Hello World
---
<div class="dynamic-content">
<h1>{{page.title}}</h1>
<p>Welcome to {{siteName}}</p>
<p>Custom variable: {{page.metadata.custom_var}}</p>
<h2>All Pages</h2>
<ul>
{{#each pages}}
<li><a href="{{../baseUrl}}{{path}}">{{title}}</a></li>
{{/each}}
</ul>
</div>
Frontmatter Support
All file types support YAML frontmatter for metadata:
---
title: Page Title
published: 2024-10-07T10:00:00 # For posts
nav_order: 1 # For pages
layout: custom # Optional: specify layout
description: Custom description # For meta tags
template: idea # Optional: use templates/idea.hbs
custom_field: Any value # Custom metadata
---
When to Use Each Format
Use Markdown (.md) when:
- Writing blog posts or articles
- You want simple, readable source files
- You need basic formatting (headers, lists, links, code blocks)
- You want to focus on content, not markup
Use HTML (.html) when:
- You need precise control over the markup
- You want to use specific CSS classes or IDs
- You're creating complex layouts or components
- You have existing HTML content to integrate
Use Handlebars (.hbs) when:
- You need dynamic content generation
- You want to iterate over data (pages, posts, custom arrays)
- You need conditional rendering
- You want to create reusable, data-driven templates
Template Variables in HBS Files
HBS files have access to all template variables:
For Posts (posts/*.hbs)
{{post.title}} - Post title
{{post.date}} - Publication date
{{post.content}} - Post content
{{post.metadata.custom_field}} - Custom metadata
{{siteName}} - Site name
{{baseUrl}} - Base URL
{{#each pages}}...{{/each}} - Loop through pages
For Pages (pages/*.hbs)
{{page.title}} - Page title
{{page.content}} - Page content
{{page.metadata.custom_field}} - Custom metadata
{{siteName}} - Site name
{{baseUrl}} - Base URL
{{#each pages}}...{{/each}} - Loop through all pages
Advanced Examples
Markdown Page Using a Custom Template
You can keep writing Markdown while still opting into a bespoke Handlebars template. Create templates/idea.hbs:
<section class="idea">
<h1>{{page.title}}</h1>
<div class="body">{{{page.content}}}</div>
</section>
Then reference it from a Markdown file with the template frontmatter key (extension optional):
---
title: Idea Vault
template: idea # Looks for templates/idea.hbs
layout: minimal # Layout still wraps the template
---
Every idea starts here.
Statik loads templates/idea.hbs (or templates/custom/idea.hbs if you provide a nested path) and renders the Markdown content with the same variables available to .hbs files. If the template override is missing, Statik falls back to templates/page.hbs.
HTML Post with Custom Layout
posts/landing.html
---
title: Product Launch
published: 2024-10-07T10:00:00
layout: landing
---
<section class="hero">
<h1 class="hero-title">Introducing Our New Product</h1>
<p class="hero-subtitle">The future of productivity</p>
<button class="cta-button">Learn More</button>
</section>
<section class="features">
<div class="feature">
<h3>Fast</h3>
<p>Lightning-fast performance</p>
</div>
<div class="feature">
<h3>Simple</h3>
<p>Easy to use interface</p>
</div>
</section>
Dynamic HBS Page with Conditional Logic
pages/archives.hbs
---
title: Archives
nav_order: 5
---
<div class="archives">
<h1>{{page.title}}</h1>
{{#if posts}}
<p>Total posts: {{posts.length}}</p>
<div class="posts-by-year">
{{#each posts}}
<article class="post-summary">
<time>{{formatDate date format="yyyy-MM-dd"}}</time>
<h2>
<a href="{{../baseUrl}}{{path}}">{{title}}</a>
</h2>
{{#if metadata.description}}
<p>{{metadata.description}}</p>
{{/if}}
</article>
{{/each}}
</div>
{{else}}
<p>No posts yet.</p>
{{/if}}
</div>
Mixed Content Site Structure
You can mix and match file types in the same site:
my-site/
├── posts/
│ ├── regular-post.md # Standard markdown
│ ├── custom-post.html # Custom HTML
│ └── dynamic-post.hbs # Dynamic template
├── pages/
│ ├── about.md # Markdown page
│ ├── portfolio.html # HTML portfolio
│ └── archives.hbs # Dynamic archives
└── config.json
Handlebars Helpers
All HBS files have access to these helpers:
formatDate
Format dates with custom patterns:
{{formatDate post.date}} # Default: MMMM dd, yyyy
{{formatDate post.date format="yyyy-MM-dd"}} # Custom format
{{formatDate post.date format="MMM d, yyyy"}} # Oct 7, 2024
excerpt
Create excerpts from content:
{{excerpt post.content}} # Default: 30 words
{{excerpt post.content words=50}} # Custom length
include
Include partials:
{{include "partials/sidebar.hbs"}}
datasource
All templates receive a datasource object that mirrors the generated JSON files, so you can render galleries, quote blocks, or entity lists during server-side rendering without additional fetches:
{{#each datasource.images}}
<img src="{{src}}" alt="{{alt}}">
{{/each}}
eq (equality)
Conditional comparison:
{{#if (eq page.title "Home")}}
<p>Welcome to the homepage!</p>
{{/if}}
Best Practices
-
Choose the Right Format
- Default to Markdown for most content
- Use HTML for specific design needs
- Use HBS for dynamic/data-driven content
-
Use Frontmatter Consistently
- Always include
titlemetadata - Add
descriptionfor better SEO - Specify
layoutwhen using custom layouts
- Always include
-
Keep Logic in Templates
- Put complex logic in
.hbsfiles - Keep
.htmlfiles for static content - Use Markdown for writing-focused content
- Put complex logic in
-
Organize by Purpose
- Group similar file types together
- Use descriptive filenames
- Document custom metadata fields
Processing Order
When Statik processes your files:
- Frontmatter is extracted (all file types)
- Content is processed:
.md→ Markdown → HTML.html→ Used as-is.hbs→ Rendered with template variables
- Layout is applied (if specified)
- HTML formatting applied (minification or beautification, based on config)
- Final HTML is written to output directory
This gives you maximum flexibility in authoring while maintaining a consistent output structure.
HTML Output Formatting
Statik provides options to control how the generated HTML is formatted. By default, Handlebars templates can produce HTML with excessive whitespace. You can configure the output format in your config.json:
Configuration Options
Add an html section to your config.json:
{
"siteName": "My Site",
"baseUrl": "https://example.com",
"description": "My description",
"author": "Your Name",
"html": {
"format": "MINIFY",
"indentSize": 2
}
}
Format Options
DEFAULT (default)
No post-processing. HTML is output as generated by Handlebars.
"html": {
"format": "DEFAULT"
}
MINIFY (recommended for production)
Reduces whitespace and file size using jsoup's HTML parser. Uses proper DOM parsing instead of regex for safe, reliable minification.
- Collapses excessive whitespace in text nodes
- Removes unnecessary whitespace between elements
- Preserves formatting in
<pre>,<code>,<textarea>,<script>, and<style>tags - Handles edge cases safely (inline event handlers, CDATA sections, etc.)
- Properly parses HTML structure for safe minification
"html": {
"format": "MINIFY"
}
Before minification:
<html>
<body>
<div>
<h1>Title</h1>
<p>Content here</p>
</div>
</body>
</html>
After minification:
<!doctype html>
<html lang="en">
<head>...</head>
<body><div><h1>Title</h1><p>Content here</p></div></body>
</html>
Note: jsoup adds minimal newlines after structural tags like <!doctype>, <html>, <head> for readability, but removes excessive whitespace elsewhere.
BEAUTIFY (useful for debugging)
Formats HTML with proper indentation for readability. Uses jsoup (already a project dependency) for parsing and formatting.
"html": {
"format": "BEAUTIFY",
"indentSize": 2
}
indentSize: Number of spaces for each indentation level (default: 2)
Before beautification:
<html><body><div><h1>Title</h1><p>Content here</p></div></body></html>
After beautification:
<html>
<body>
<div>
<h1>Title</h1>
<p>Content here</p>
</div>
</body>
</html>
Recommendations
- Production sites: Use
MINIFYfor smaller file sizes and faster load times - Development: Use
BEAUTIFYorDEFAULTto inspect generated HTML - Debugging templates: Use
BEAUTIFYto see the structure clearly
Performance Impact
- MINIFY: Uses jsoup (already a dependency), minimal overhead, safe HTML parsing
- BEAUTIFY: Uses jsoup (already included), minimal overhead
- DEFAULT: No processing, fastest build times
The HTML formatter processes each page after template rendering and layout application, ensuring consistent output across all pages in your site.
Why jsoup Instead of Regex?
Both MINIFY and BEAUTIFY use jsoup for HTML processing because:
- Safety: jsoup properly parses HTML as a DOM tree, avoiding edge cases that break regex-based approaches
- Edge cases handled: Inline event handlers (
onclick="alert('>')"), CDATA sections, complex nesting - No bugs: Regex can't parse HTML reliably since HTML is not a regular language
- Already included: jsoup is already a Statik dependency for datasource generation
- Battle-tested: jsoup is a mature, widely-used library
While a regex-based minifier might seem lighter, it can produce broken HTML on edge cases. jsoup ensures your HTML is always valid and properly structured.