January 1, 2026
Your Post Title Here

How to write a post
- Copy this file to
content/posts/<slug>.md. The filename (minus.md) becomes the URL slug, e.g.content/posts/my-new-post.md→/blog/my-new-post. Use lowercase, hyphen-separated slugs. - Fill in the frontmatter block at the top (see field reference below).
- Write the body in standard Markdown underneath the closing
---. - Set
draft: truewhile you're still working on it — draft posts are excluded fromgetAllPosts()/getPostBySlug()and won't appear on/blogor be statically generated. Remove it (or setdraft: false) when you're ready to publish. - Commit and push. Posts are read from disk at build time
(
src/lib/posts.ts) — no CMS, no database.
Frontmatter reference
| Field | Type | Required | Notes |
|---|---|---|---|
title | string | yes | Falls back to "Untitled" if omitted. Rendered as the <h1> on the post page and the card heading on /blog. Keep it short — the blog index card is a fixed square and clamps the title to 2 lines. |
date | YYYY-MM-DD | yes | Drives sort order on /blog (newest first) and the displayed date. Anything new Date() can parse works, but stick to YYYY-MM-DD for consistency. |
tags | array of strings | no | Rendered as pills on the home page's "Recent posts" cards (PostMeta with its default showTags), but hidden on the /blog index cards (showTags is false there) and not shown on the post page itself. |
excerpt | string | no* | Shown as the subtitle on the /blog index card (clamped to 2 lines) and used as the <meta name="description"> on the post page. Effectively required for a good card/SEO, defaults to "" if omitted. |
draft | boolean | no | Defaults to false. When true, the post is filtered out of every listing and its page returns 404 (generateStaticParams/getPostBySlug both skip it). |
image | string (path) | no | Absolute path to the card thumbnail shown on /blog, e.g. /posts/your-image.png. Not shown on the individual post page — add the image in the body too (see below) if you want it there. Omit for a text-only card. |
Adding a thumbnail / images
Drop image files in public/posts/ and reference the path (starting with /posts/)
in the image frontmatter field to set the /blog index card thumbnail:
image: /posts/your-image.png
To also show an image inside the post body itself, add a Markdown image tag wherever you want it to appear:

These are independent — the frontmatter image only controls the index card
thumbnail, and body images only render on the post's own page. If a post has no
image frontmatter field, the card simply skips the thumbnail and shows only text.
Always include descriptive alt text on body images.
Markdown support
Post bodies render through react-markdown with remark-gfm (see
src/components/PostMarkdown.tsx), so you get:
- Headings
#/##/###(mapped down one level visually —#renders like an<h2>) - Paragraphs, bold/italic, links (open in a new tab automatically)
- Bullet and numbered lists, nested lists
- Blockquotes (
>) - Inline
codeand fenced code blocks (plain monospace styling — no syntax highlighting is wired up, so don't rely on language-specific colors) - Tables, strikethrough (
~~text~~), and autolinks via GFM - Horizontal rules (
---) - Images (
) — full-width, rounded corners
Example body
## A section heading
Some regular paragraph text with a [link](https://example.com) and **bold**.
- A bullet
- Another bullet
