Blogatto

A Gleam framework for building static blogs with Lustre and Markdown.
Blogatto generates your entire static site from a single configuration: blog posts from markdown with frontmatter, static pages from Lustre views, RSS feeds, sitemaps, and robots.txt — all rendered via Maud components.
Features
- Blog posts from markdown files with YAML frontmatter
- Multilingual posts via
index-{lang}.mdfile naming convention - Static pages from Lustre view functions
- RSS feed generation with customizable filtering and serialization
- Sitemap XML generation with alternate language links
- Robots.txt generation
- Static asset copying
- Custom Maud components for markdown rendering
- Configurable blog post templates
- Dev server with file watching, auto-rebuild, and live reload
Installation
gleam add blogatto@2
gleam add lustre@5
Quick start
import blogatto
import blogatto/config
import blogatto/config/feed
import blogatto/config/markdown
import blogatto/config/robots
import blogatto/config/sitemap
import blogatto/error
import blogatto/post.{type Post}
import gleam/io
import gleam/list
import gleam/time/timestamp
import lustre/attribute
import lustre/element.{type Element}
import lustre/element/html
const site_url = "https://example.com"
pub fn main() {
// Markdown config with custom heading component
let md =
markdown.default()
|> markdown.markdown_path("./blog")
|> markdown.route_prefix("blog")
|> markdown.h1(fn(id, children) {
html.h1([attribute.id(id), attribute.class("post-title")], children)
})
// RSS feed
let rss =
feed.new("My Blog", site_url, "My personal blog")
|> feed.language("en-us")
|> feed.generator("Blogatto")
// Build configuration
let cfg =
config.new(site_url)
|> config.output_dir("./dist")
|> config.static_dir("./static")
|> config.markdown(md)
|> config.route("/", home_view)
|> config.feed(rss)
|> config.sitemap(sitemap.new("/sitemap.xml"))
|> config.robots(robots.RobotsConfig(
sitemap_url: site_url <> "/sitemap.xml",
robots: [
robots.Robot(
user_agent: "*",
allowed_routes: ["/"],
disallowed_routes: [],
),
],
))
case blogatto.build(cfg) {
Ok(Nil) -> io.println("Site built successfully!")
Error(err) -> io.println("Build failed: " <> error.describe_error(err))
}
}
fn home_view(posts: List(Post(Nil))) -> Element(Nil) {
let sorted =
list.sort(posts, fn(a, b) { timestamp.compare(b.date, a.date) })
html.html([], [
html.head([], [html.title([], "My Blog")]),
html.body([], [
html.h1([], [element.text("My Blog")]),
html.ul(
[],
list.map(sorted, fn(p) {
html.li([], [
html.a([attribute.href("/blog/" <> p.slug)], [
element.text(p.title),
]),
])
}),
),
]),
])
}
Running gleam build will generate the dist directory with the following structure:
dist/
├── blog/
│ └── my-post/
│ └── index.html
├── index.html
├── robots.txt
├── sitemap.xml
└── feed.xml
Dev server
Blogatto includes a built-in development server that watches your source files for changes, automatically rebuilds the site, and live-reloads the browser via SSE.
Create a separate dev entrypoint module (e.g., src/dev.gleam):
import blogatto/dev
import blogatto/error
import gleam/io
import my_blog // your module that exposes your blogatto config
pub fn main() {
let cfg = my_blog.config()
case
cfg
|> dev.new()
|> dev.build_command("gleam run -m my_blog")
|> dev.port(3000)
|> dev.start()
{
Ok(Nil) -> io.println("Dev server stopped.")
Error(err) -> io.println("Dev server error: " <> error.describe_error(err))
}
}
Run with: gleam run -m dev
The dev server will:
- Perform an initial build by running the configured build command
- Serve the output directory over HTTP at
http://127.0.0.1:3000 - Watch
src/, markdown paths, and static assets for changes - Debounce rapid file changes (~300ms) and rebuild automatically
- Live-reload the browser on successful rebuilds
Configuration
| Option | Default | Description |
|---|---|---|
build_command | "gleam run" | Shell command to rebuild the site |
port | 3000 | HTTP server port |
host | "127.0.0.1" | Bind address |
live_reload | True | Inject live-reload script into HTML responses |
Note for Linux users: The file watcher requires
inotify-toolsto be installed.
Documentation
Full documentation is available at blogat.to, covering blog post structure, configuration, markdown components, static pages, RSS feeds, sitemaps, dev server, and error handling.
API reference is on HexDocs.
Development
gleam build # Compile the project
gleam test # Run the tests
gleam format src test # Format code
License
Blogatto is licensed under the MIT License. See LICENSE for details.