This guide walks through creating a static site with the SSG. The example site is the best reference — it is built by the same tool and documents the project itself.
mysite/
pages/ *.md, *.html content pages with optional frontmatter
layouts/ *.html HTML layout templates
public/ * static assets copied as-is
All three directories are optional (layouts/ and public/ can be
omitted), but pages/ is where your content lives.
Each page is a markdown (.md) or HTML (.html) file. Markdown is
rendered to HTML; HTML files pass through unchanged.
Every page can have YAML frontmatter at the top:
---
title: My Page
layout: default
date: 2026-06-08
collections: posts
description: "A short description."
---
Content goes here. Markdown works.
| Field | Purpose |
|---|---|
title |
Page title, available as {{title}} in layouts |
layout |
Layout template to use (default: "default") |
collections |
Comma-separated collection names for {{#each}} listing |
date |
Publication date (used for sorting collections) |
| Any other field | Available as a variable in layouts |
File paths become clean URLs:
index.md → /blog/index.md → /blogblog/my-post.md → /blog/my-postThe output writes /blog/my-post/index.html so browsers see /blog/my-post.
Layouts are HTML files in layouts/. They use four mechanisms:
Reference other layouts by filename (without .html):
<Header />
Wrapper components inject their inner content into a <slot>:
<BaseLayout>
<Header />
<slot/>
<Footer />
</BaseLayout>
Components can nest — BaseLayout might wrap everything inside a shared
DOCTYPE, <head>, and <body>.
Use {{fieldName}} to insert frontmatter values:
<title>{{title}} — My Site</title>
The special variable {{content}} holds the rendered page body.
The page body is injected where <slot/> appears. A fallback can be
provided:
<slot>This shows when no content is provided.</slot>
Iterate over a collection of pages:
<ul>
{{#each posts sort=date}}
<li>
<a href="{{route}}">{{title}}</a>
<span>{{date}}</span>
</li>
{{/each}}
</ul>
sort=field sorts descending by that fieldexcludeCurrent=true (default) excludes the current page from the loop{{fieldName}} resolves against each item's frontmatter{{route}} is the cleaned URL path of the itemCollections group pages together. Add collections: posts to a page's
frontmatter to include it in the posts collection.
A page can belong to multiple collections:
collections: posts, featured
Collections are consumed by {{#each}} loops in layouts. The canonical
pattern is an index page that lists all pages in a collection — see
example/pages/blog/index.md.
Files in public/ are copied to the output root as-is. Use this for
CSS, JavaScript, images, fonts, and favicon.ico.
public/
style.css → dist/style.css
favicon.svg → dist/favicon.svg
images/logo.png → dist/images/logo.png
These files are never parsed or templated.
# One-shot build
bun run src/main.ts build \
--source mysite/pages \
--output mysite/dist \
--layouts mysite/layouts \
--public mysite/public
# Dev server with live reload (port 3000 by default)
bun run src/main.ts build \
--source mysite/pages \
--output mysite/dist \
--layouts mysite/layouts \
--public mysite/public \
--dev
# Custom port
bun run src/main.ts build ... --dev --port 8080
--source and --output are required--layouts and --public are optionalA two-page site in six files:
mysite/pages/index.md
---
title: Home
layout: Home
---
Welcome to my site.
mysite/pages/about.md
---
title: About
---
I write stuff.
mysite/layouts/default.html
<!DOCTYPE html>
<html><head><title>{{title}}</title></head>
<body><slot/></body></html>
mysite/layouts/Home.html
<default>
<h1>{{title}}</h1>
<slot/>
<nav><a href="/about">About</a></nav>
</default>
mysite/public/style.css
body { font-family: sans-serif; }
Build it:
bun run src/main.ts build --source mysite/pages --output mysite/dist \
--layouts mysite/layouts --public mysite/public
Output:
mysite/dist/
index.html ← wraps index.md in Home.html → default.html
about/index.html ← wraps about.md in default.html
style.css ← copied from public/
That's it. For a full real-world example with collections, blog listings, and a design system, see the example site.