Contents9

2026 update: A post ported over from the old VuePress blog. VuePress 1.x is no longer a choice for new builds, so the body here is kept as an archive of “Markdown extension notes from 2021”. The ideas behind containers and frontmatter still carry over to MDX + Astro / Next.js, so read it as comparison material.

Back in 2021, the precursor blog to Aulvem ran on VuePress 1.x. Plain Markdown couldn’t quite handle table of contents, callout blocks, and article metadata, so I ended up touching VuePress’s own Markdown extensions more than once. This post is a cleaned-up version of those configuration notes, viewed from 2026.

VuePress 1.x is for all practical purposes unmaintained as of 2026 (citation needed — confirm current status in the official repo), and I wouldn’t recommend it for new projects. Even so, the thinking behind containers and embedded components has carried over into the MDX side of the world, so there’s still some value in leaving the calls I made then on record.

Short answer — split VuePress Markdown extensions into three layers

Short answer: VuePress 1.x Markdown extensions are easier to think about when split into three layers — “embedded Vue components”, “named containers (custom blocks)”, and “frontmatter”.

The reason is that each layer involves different config files and responsibilities. Components belong on the .vue side, containers belong in the index.js plugin config plus styles, and frontmatter belongs in article metadata and the template — keeping them separate stops you from losing track of which setting goes where.

As a concrete split, repeating elements like a table of contents or footer become components, callout boxes become containers, and titles, categories, and tags go in frontmatter.

A caveat: as of 2026, MDX + a framework (Astro / Next.js) handles the same roles more directly, so from here on, read this as an archive of “how it worked in VuePress 1.x at the time”.

extendMarkdown — the entry point for overriding markdown-it behavior

Short answer: markdown.extendMarkdown in config.js lets you touch the markdown-it instance directly.

VuePress 1.x parses Markdown with markdown-it internally, and extendMarkdown is the hook for overriding that configuration. Line break handling, link target, and the syntax highlighting area all got tuned here.

The setting I actually used is below. It enables breaks: true, which turns editor line breaks into <br>.

markdown: {
  extendMarkdown: md => {
    md.set({ breaks: true })
  }
},

A caveat: breaks: true is convenient for Japanese blog writing where each sentence is broken to its own line, but it doesn’t sit well with English-style paragraphs written as one block. It’s a setting you pick based on how the article is written, not something that should be on by default.

Embedding Vue components inside Markdown

Short answer: in VuePress you can write a Vue component directly inside a Markdown file.

The reason is that VuePress’s build converts Markdown to a Vue SFC before rendering. Write the tag with the same syntax you’d use inside a .vue file, and it gets resolved as a component at build time.

<ComponentName />

Repeating blocks (related links under an article, footers, table of contents) were turned into components this way.

A caveat: Markdown and JSX-ish syntax now coexist, so the writer’s prior knowledge requirement goes up. It works as a design when the writers are editors only and operations are small — not so much if external writers are involved. MDX has the same trade-off.

Defining custom blocks with named containers

Short answer: the ::: type title::: syntax lets you define blocks with custom styles.

VuePress 1.x ships with tip / warning / danger by default, and you can add more types via the container plugin config in index.js. The body inside is regular Markdown.

The syntax looks like this.

::: displayType titleName
Markdown can be used inside.
::: 

When written, it renders as a block with CSS tied to the type name. Example:

titleName

Markdown can be used inside.

The display type is defined in the plugin config in index.js. type is the display type name, defaultTitle is the label used when no title is given, and the '/' key is the prefix for switching per language (Japanese is the default for this site, so leaving it as / is enough).

src   
└─ .vuepress
   └─ theme
      └─ index.js
module.exports = (options, ctx) => {
  return {
    plugins: [
      ['container', {
        type: 'typeName',
        defaultTitle: {
          '/': 'defaultTitleName',
        }
      }],
    ]
  }
}

Style definitions lived on the custom-blocks.styl side. Add a selector for the new type name next to tip / warning / danger.

src   
└─ .vuepress
   └─ theme
      └─ styles
         └─ custom-blocks.styl
  &.tip
  &.warning
  &.danger

A caveat: containers are a markdown-it-container plugin mechanism, so they’re a lightweight Markdown-side extension. If you want to call arbitrary components JSX-style, leaning toward embedded components or MDX is the better fit.

frontmatter — article metadata at the top of Markdown

Short answer: a YAML frontmatter defines the article’s title, publication date, category, tags, image, and so on.

The VuePress template and plugin-blog’s frontmatters config read those declared values and reflect the metadata into list pages and individual pages. The frontmatter I was using at the time looked like this.

title: [How to] Build your own blog with VuePress/plugin-blog — Markdown edition
date: 2021-03-02
category:
  - skill
tag: 
  - VuePress
image: {{Path}}
location: japan 

tag shows up in $tag and $currentTag and lands on the list pages when its name matches a registered entry in plugin-blog’s frontmatters. category works without registration, but I aligned it by hand for internal classification. image is the OG image and thumbnail.

A caveat: frontmatter key names differ across frameworks. VuePress 1.x used date / tag / category (singular), while current Aulvem (Astro) uses pubDate / tags / category, and migrations stumble here often.

Markdown format comparison (2026 view)

LensPlain MarkdownVuePress 1.x MarkdownMDX
BaseCommonMark / GFMmarkdown-it + extensionsMarkdown + JSX
Component embeddingnot availableVue component written inlinearbitrary JSX / components
Custom blocksblockquotes / lists, roughly::: type syntax (container)replaced by JSX components
frontmattertool-dependentyes (YAML)yes (tool-dependent)
Main use in 2026README / Issuemaintaining existing VuePress 1.x sitesstatic sites on Astro / Next.js and similar

VuePress 1.x containers and embedded components were an early take on “extending Markdown toward JSX”, and MDX now plays roughly the same role (citation needed — confirm MDX’s historical positioning in its official spec).

FAQ

Q. How is VuePress Markdown different from plain Markdown? A. VuePress emits an extended flavor built on markdown-it, adding embedded Vue components, named containers (::: tip and friends), frontmatter, and PrismJS-based syntax highlighting. It’s more expressive than plain Markdown, but the difference is that the build assumes a Vue runtime.

Q. How are VuePress containers (::: tip etc.) different from MDX components? A. Containers are a syntax implemented via the markdown-it-container plugin — a lightweight extension that stays close to Markdown. MDX is a separate spec that lets you write JSX directly inside Markdown, so you can embed arbitrary React / Vue components as expressions. Containers are custom blocks, MDX is full JSX — the scope of responsibility is different.

Q. Is it worth picking VuePress 1.x for a new project in 2026? A. Barely. VuePress 1.x is effectively unmaintained, and if you’re staying on Vue, VitePress has more material and more extension room; for a more general SSG, Astro or Next.js + MDX is the safer pick (citation needed). Keeping VuePress 1.x around only for maintenance of an existing site is the realistic path.

Q. Is breaks: true in extendMarkdown necessary? A. It depends on taste. The setting converts editor line breaks into <br>, which is convenient for Japanese blog writing where each sentence ends on its own line. For English-style writing where paragraphs are treated as one block, it isn’t needed and tends to introduce unintended line breaks.

If I were doing the same thing today

Short answer: if you want to stay on Vue, VitePress is the closest fit; if you want something framework-agnostic, Astro + content collections + MDX gets you a similar experience.

The reason is that VuePress 1.x maintenance has all but stopped, while VitePress is the successor the Vue team put together, and Astro was designed to handle Markdown / MDX natively. Aulvem itself ended up moving to Astro.

A caveat: the exact state of each tool as of 2026 — latest version, maintenance posture, default features — is something to check against the official documentation (citation needed). This post is, at heart, a note “someone who wrote on VuePress 1.x in 2021 went back and tidied up later” — not a reference for choosing a current tool.

Closing

VuePress 1.x’s Markdown was an extension on top of plain Markdown — component embedding, named containers, and frontmatter on top — and at the time it had enough expressive power for small-to-medium documentation and blog use.

As of 2026, that role has shifted to MDX + Astro / Next.js + content collections. The thinking behind the VuePress era (keep article metadata in frontmatter, write callout blocks with a syntax, componentize what gets reused) still translates straight across to the new stack, so this post is left up as a design record from that time.