Skip to content
Wei Li
Go back

Writing Blog Posts in AsciiDoc

Edit page

This post is written in AsciiDoc instead of Markdown, to prove out the site’s custom content loader end to end — and this time, to actually put AsciiDoc’s own syntax through its paces instead of writing Markdown with a different file extension.

Why AsciiDoc?

AsciiDoc has a few things built in that Markdown leaves to extensions or plain HTML. See A comparison table below for a rundown, or jump straight to Admonition blocks if you just want to see the callout boxes.

A comparison table

Feature Markdown AsciiDoc

Admonitions

Blockquote convention only

Built-in NOTE/TIP/WARNING/IMPORTANT/CAUTION blocks

Table of contents

Plugin required

toc::[] macro, built in

Cross-references

Manual anchor links

<<id>> resolves automatically, even across files

Definition lists

Not standard

Built-in Term:: Definition syntax

Admonition blocks

AsciiDoc has five built-in admonition types, each with its own icon and meaning:

This whole post renders through a hand-written Astro content loader (src/loaders/asciidoc.ts, using the asciidoctor npm package), feeding into the exact same post page that Markdown posts use.
You can cross-reference any section by its auto-generated ID — see how this page links back up to A comparison table.
The loader sets rendered.html on the content store entry, which is the specific field that makes render() and <Content /> work for a post regardless of which loader produced it.
Asciidoctor’s output doesn’t go through Astro’s Markdown pipeline, so it doesn’t get Astro’s Shiki highlighting for free. The loader runs Shiki over the code blocks itself at build time, with the same themes as the Markdown posts — keep the two in sync if you change one.
Don’t name a Markdown post and an AsciiDoc post the same thing (e.g. my-post.md and my-post.adoc) — both collections share the same src/content/posts/ directory and route namespace, so their generated IDs would collide.

Definition list

Content Layer API

The Astro API that lets a collection be backed by any data source, not just the built-in Markdown/MDX/YAML/JSON loaders.

rendered.html

The field a custom loader sets so render() and <Content /> behave the same as they do for Markdown entries.

getAllPosts()

A small helper in src/utils/getAllPosts.ts that merges the posts and postsAdoc collections so pages don’t care which format a post uses.

A sidebar

Why keep two separate collections instead of one loader for both formats?

Reusing Astro’s existing glob() Markdown loader for .md files means Markdown posts keep Shiki syntax highlighting and every other feature of Astro’s built-in Markdown pipeline, completely unaffected by the AsciiDoc work. A single merged loader would have meant reimplementing that pipeline by hand just to keep it working.

A code example with callouts

---
const pageTitle = "Hello"; (1)
---
<h1>{pageTitle}</h1> (2)
1 Component frontmatter runs once, at build time.
2 The template below the fence renders it into HTML.

Inline formatting

AsciiDoc supports monospace, bold, italic, and combinations like bold italic. You can also mark up Ctrl+S to save, click Submit, or open File  Save As…​ — none of which have a plain Markdown equivalent without raw HTML.

Astro was designed to feel familiar and approachable regardless of skill level or past experience with web development.

— Astro Documentation
https://docs.astro.build/en/concepts/why-astro/

Wrapping up

That’s it — this post shows up on the blog listing, its tag pages, and the RSS feed exactly like a Markdown post would, because both collections share one schema and get merged by getAllPosts() before anything renders them.


Edit page
Share this post:

Previous Post
Getting Started with Astro: Pages, Posts, and First Impressions
Next Post
Mastering Java Streams: From Basics to Advanced