Docs · Content & SEO

Writing a Blog Post

Front matter reference, the write-post skill, and what the app generates for you automatically.

A post is a markdown file in content/posts/. Drop the file, commit, deploy. There's no database table, no migration, and no admin UI to log into: your writing is reviewed, versioned, and shipped exactly like your code, in whatever editor you already use.

content/posts/charging-for-your-app.md   ->   /blog/charging-for-your-app

The fastest way to write one is the write-post skill in Claude Code; see The write-post Skill for its full walkthrough. This page is the front-matter reference it, and you, draw from.

Front matter fields

title, description, and pub_date are required. Everything else is optional.

Field What it does
title The <h1>, the <title>, the feed entry, the JSON-LD headline
description Meta description, social card text, feed summary, search snippet
pub_date Sort order and datePublished. Posts list newest first
updated_date Shown on the post, published as dateModified (a freshness signal)
author Defaults to founder_name in config/initializers/app_identity.rb
tags Tag chips, the /blog/tags/<tag> pages, JSON-LD keywords
draft true keeps it out of the index, the feeds, and the sitemap
image Per-post social share image. Falls back to /og.png
cta_title, cta_body, cta_label, cta_url The bottom-of-post call to action

content/posts/hello-world.md is a worked example carrying every field.

Why the CTA lives in front matter, not in a view

So the shipped partial carries no marketing copy of its own. Each post supplies its own call-to-action; when it doesn't, the fallback builds itself from your app_name and app_description. That means the blog works correctly the moment you rebrand with bootstrap-app, with nothing left over to find and delete.

What you get without doing anything

Every post automatically gets a canonical URL built from its path, full Open Graph and Twitter tags, BlogPosting and BreadcrumbList structured data, an entry in /sitemap.xml with a lastmod, an entry in both /feed.xml and /feed.json, a line in /llms.txt with its full text in /llms-full.txt, a raw markdown alternate at /blog/<slug>.md, and server-rendered syntax highlighting with zero JavaScript on the page. None of this is something you add by hand. See SEO and Structured Data for how it's built.

Style

Plain and concrete, matching the voice of the site's own landing page. No em dashes anywhere in copy or metadata; a spec enforces this across both the blog and this docs site, so use a colon, a comma, or a full stop instead. Start body headings at ##, never #, since # renders from the front-matter title.

Checking your work

bin/dev
open http://localhost:3000/blog

Then confirm the machine-readable surfaces: /sitemap.xml, /feed.xml, /feed.json, /llms.txt, and /blog/<slug>.md.

Next

See exactly what SEO machinery is generated for you, and why: SEO and Structured Data.