Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Atom feeds

Blogatto generates Atom 1.0 feeds from your blog posts. You can configure multiple feeds with different filters (e.g., one per language) and customize how posts are serialized into feed entries. Atom feeds work alongside RSS feeds — both can be generated from the same build.

Basic setup

import blogatto/config
import blogatto/config/feed/atom
import gleam/time/timestamp

let atom_feed =
  atom.new(
    id: "https://example.com/",
    title: atom.PlainText("My Blog"),
    updated: timestamp.system_time(),
  )
  |> atom.subtitle("My personal blog")
  |> atom.author(atom.Person(
    name: "Jane Doe",
    email: option.None,
    uri: option.None,
  ))

let cfg =
  config.new("https://example.com")
  |> config.atom_feed(atom_feed)

This generates dist/atom.xml containing all blog posts with auto-generated summaries. The summary is built from the post excerpt, whose length is controlled by post.excerpt_len() (default: 200 characters).

AtomFeed fields

Required fields (passed to atom.new())

FieldTypeDescription
idStringUnique feed identifier (typically a URL or URN)
titleTextFeed title (PlainText, Html, or XHtml variant)
updatedTimestampLast time the feed was updated

Optional fields (set via builder functions)

FieldSetterDefaultDescription
outputatom.output()"/atom.xml"Output path relative to output_dir
authorsatom.author()[]Feed authors (prepends)
linkatom.link()NoneFeed link (e.g., the homepage)
categoriesatom.category()[]Feed categories (prepends)
contributorsatom.contributor()[]Feed contributors (prepends)
generatoratom.generator()NoneGenerator program
iconatom.icon()NoneSmall icon URL
logoatom.logo()NoneLarger logo URL
rightsatom.rights()NoneRights/copyright information
subtitleatom.subtitle()NoneFeed subtitle or tagline
filteratom.filter()NoneInclude/exclude posts
serializeatom.serialize()NoneCustom entry serialization

Filtering posts

Use the filter function to control which posts appear in a feed. The function receives FeedMetadata (from blogatto/config/feed) containing the post and its URL path:

import blogatto/config/feed
import gleam/option

// Only include English posts
let atom_feed =
  atom.new(
    id: "https://example.com/",
    title: atom.PlainText("My Blog"),
    updated: timestamp.system_time(),
  )
  |> atom.filter(fn(meta: feed.FeedMetadata(Nil)) {
    meta.post.language == option.None
    || meta.post.language == option.Some("en")
  })

Custom serialization

Override how posts become feed entries with the serialize function:

import blogatto/config/feed
import blogatto/config/feed/atom
import gleam/option.{None, Some}

let atom_feed =
  atom.new(
    id: "https://example.com/",
    title: atom.PlainText("My Blog"),
    updated: timestamp.system_time(),
  )
  |> atom.serialize(fn(meta: feed.FeedMetadata(Nil)) {
    atom.AtomFeedItem(
      id: meta.url,
      title: atom.PlainText(meta.post.title),
      updated: meta.post.date,
      authors: [
        atom.Person(name: "Jane Doe", email: None, uri: None),
      ],
      content: None,
      link: Some(atom.Link(
        href: meta.url,
        rel: Some("alternate"),
        content_type: Some("text/html"),
        hreflang: None,
        title: None,
        length: None,
      )),
      summary: Some(atom.PlainText(meta.post.excerpt)),
      categories: [],
      contributors: [],
      published: Some(meta.post.date),
      rights: None,
      source: None,
    )
  })

Multiple feeds

Call config.atom_feed() multiple times to generate separate feeds:

let en_feed =
  atom.new(
    id: "https://example.com/atom.xml",
    title: atom.PlainText("My Blog (English)"),
    updated: timestamp.system_time(),
  )
  |> atom.filter(fn(meta) {
    meta.post.language == option.None
    || meta.post.language == option.Some("en")
  })

let it_feed =
  atom.new(
    id: "https://example.com/atom-it.xml",
    title: atom.PlainText("Il mio Blog (Italiano)"),
    updated: timestamp.system_time(),
  )
  |> atom.output("/atom-it.xml")
  |> atom.filter(fn(meta) {
    meta.post.language == option.Some("it")
  })

let cfg =
  config.new("https://example.com")
  |> config.atom_feed(en_feed)
  |> config.atom_feed(it_feed)

FeedMetadata

The FeedMetadata(msg) type (from blogatto/config/feed) passed to filter and serialize functions:

FieldTypeDescription
pathStringURL path (e.g., "/blog/my-post")
postPost(msg)The full parsed blog post (includes excerpt field)
urlStringThe absolute URL of the post

AtomFeedItem

The AtomFeedItem type returned by serialize functions:

FieldTypeDescription
idStringUnique entry identifier (required)
titleTextEntry title (required)
updatedTimestampLast update timestamp (required)
authorsList(Person)Entry authors
contentOption(Text)Full entry content
linkOption(Link)Entry link (e.g., the post URL)
summaryOption(Text)Short summary or description
categoriesList(Category)Entry categories
contributorsList(Person)Entry contributors
publishedOption(Timestamp)Original publication timestamp
rightsOption(Text)Rights information for the entry
sourceOption(Source)Reference to original feed when syndicated

Text variants

Atom text fields (title, summary, content, rights) accept three variants:

  • atom.PlainText(value) — plain text, special characters escaped on serialization
  • atom.Html(value) — HTML content (the string is treated as escaped HTML)
  • atom.XHtml(value) — XHTML content (must be a valid XHTML fragment)