You do not need to build a new screen to write a blog post. Add one Markdown file under src/content/blog/, and the build creates the post page and also reflects it in the per-language lists, the RSS feed and the sitemap. The writer manages only the body and the frontmatter; the site code handles the repeated screens and links.
Why not build an admin screen?
The main job of this blog is writing and publishing. If I built an admin screen first, I would also have to run login, permissions, a save API and an editor. Markdown, on the other hand, keeps its change history in git, and because the posts live in the same repository as the code, one build shows the published result.
So I kept the unit of input small.
A file starts with frontmatter, followed by the body and, when needed, a diagram block such as d3-flow. Repeating this structure for every post lets me use the same rendering rules without a separate editor.
One file is one post. The file name becomes the slug in the URL, so I keep using the name I first chose. Even when I edit a post, its address does not change.
What the frontmatter does
The frontmatter above the body holds the title, description, publish date and tags. If you omit lang, the post is Korean. For a post in another language, write lang and pair (the Korean original) to connect the language menu and hreflang.
---
title: Post title
description: One-line summary shown in search results
pubDate: 2026-10-24
tags: [Astro, Markdown]
---
The content schema checks the length of the title and description at build time. If a required value is missing or a language that is not allowed is used, the error appears before deployment. Instead of remembering the rules for every post, the check happens at the boundary where the content is read.
How one file becomes several results
At build time, the Astro content collection reads src/content/blog/*.md. Posts with draft: true are left out of the lists, and posts in each language are sorted by publish date, newest first. Once enough posts share the same tag, related posts are attached automatically.
The advantage of this flow is that it reduces the places where a link could be forgotten. I do not register posts in a separate list or edit the RSS file by hand. When a new post enters the collection, every result is generated from the same source.
Keep diagrams inside the body too
For long explanations, I put hand-made D3 diagrams in as code blocks. For example, the flow chart above is JSON written inside a d3-flow block. A build plugin turns it into SVG, so no external images or screenshot files are needed.
The sentences inside a diagram follow the same standard as the body. I do not put unverified numbers or operational details in it, only the names needed to explain the flow of the post. If the JSON is wrong, I can fix it from the warning in the build log.
What to check when adding a post
When writing a new post, I check only four things.
- Is the file name a unique slug made of lowercase letters, digits and hyphens?
- Are the title, description and publish date in the frontmatter?
- Did I avoid writing a
# H1in the body? - Do the D3 diagrams and Markdown syntax build correctly?
I do the last check with this command.
pnpm --filter site30cut build
Adding one post file is simple, but to keep it simple, it matters to trust the automatic links and not add manual registration. If the content lives in Markdown and the shared behavior lives in the site code, their responsibilities do not get mixed.
Frequently asked questions
Will I need to add an admin screen later?
You can consider it when the number of people who write and review posts grows, or when non-developers need to edit. If the current flow of managing posts in git is enough, an admin screen costs extra work to run the repository, permissions and editor.
Can I hide a post temporarily?
If you write draft: true in the frontmatter, the post is excluded from the build output. When you want to publish it, delete the value or change it to false and build again.
Do I need to create page code for each post?
No. The shared blog page component reads the post data from the collection. What changes from post to post is only the Markdown file and its frontmatter.