January 1, 2026

Your Post Title Here

How to write a post

  1. 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.
  2. Fill in the frontmatter block at the top (see field reference below).
  3. Write the body in standard Markdown underneath the closing ---.
  4. Set draft: true while you're still working on it — draft posts are excluded from getAllPosts()/getPostBySlug() and won't appear on /blog or be statically generated. Remove it (or set draft: false) when you're ready to publish.
  5. Commit and push. Posts are read from disk at build time (src/lib/posts.ts) — no CMS, no database.

Frontmatter reference

FieldTypeRequiredNotes
titlestringyesFalls 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.
dateYYYY-MM-DDyesDrives sort order on /blog (newest first) and the displayed date. Anything new Date() can parse works, but stick to YYYY-MM-DD for consistency.
tagsarray of stringsnoRendered 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.
excerptstringno*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.
draftbooleannoDefaults to false. When true, the post is filtered out of every listing and its page returns 404 (generateStaticParams/getPostBySlug both skip it).
imagestring (path)noAbsolute 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:

![A descriptive alt text](/posts/your-image.png)

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 code and 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 (![alt](/posts/file.png)) — 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

![A descriptive alt text](/posts/your-image.png)
© 2026 Simon Wong.