Markdown and MDX

By Syncfusion®, Inc. · Last updated

Markdown pages, frontmatter, heading IDs, import.meta.glob() (which replaced Astro.glob()), layouts for Markdown, the Sätteri and unified processors, the markdown config option, syntax highlighting with Shiki and Prism, the Code component, MDX and Markdoc.

Markdown in Astro

  • Markdown = a simple way to write formatted text with plain characters.
  • # Title makes a heading. **word** makes bold text. - item makes a list.
  • Writers like it because they can focus on words, not tags.
  • Astro turns .md files into HTML pages for you.
  • In this lesson, Asha moves her posts from .astro code into Markdown files.

Simple lesson: write content in Markdown, and Astro builds the HTML.

A Real-World Example

  • Asha's posts are long. Writing every paragraph inside <p> tags is slow.
  • Her friend Ravi wants to help write posts, but he does not know HTML.
  • In Markdown, both of them just type text, headings and lists.
  • Each .md file in src/pages becomes a page, like an .astro file.

Press ▶ Run. Then open first-post.md and change the text.

What happened:

  • The part between --- lines at the top is frontmatter: data about the page.
  • Below it, the Markdown became headings, a paragraph, bold text and a list.
  • Open HTML sent to browser. Each heading got an id, such as id="what-i-like".

What You Will Learn

In this lesson, you will learn:

  1. How Markdown pages and frontmatter work.
  2. How headings get IDs for anchor links.
  3. How to read many Markdown files with import.meta.glob().
  4. How to give Markdown pages a layout.
  5. How Astro processes Markdown, and how to change the processor.
  6. How code blocks get syntax highlighting.
  7. What MDX adds, and when to use it.

Markdown Basics

Markdown Pages

  • A .md file in src/pages becomes a page. src/pages/first-post.md builds to /first-post.
  • The same routing rules apply as for .astro files.

Frontmatter

  • Frontmatter = data at the top of a Markdown file, between two --- lines.
  • It is written in YAML: key: value on each line.
  • Common fields: title, description, pubDate, tags, layout.
  • A list in YAML is tags: [astro, markdown].

Heading IDs and Anchor Links

  • Astro gives every heading an id made from its text.
  • ## What I like gets id="what-i-like".
  • You can link straight to it: <a href="/first-post#what-i-like">.
  • This is how a table of contents jumps to a section.

Press ▶ Run. The frontmatter is read by the home page. Then open the guide and check the heading IDs in HTML sent to browser.

  • import * as guide loads the Markdown file as a module. guide.frontmatter holds its data.
  • ## Install Node.js got id="install-nodejs". The dot was dropped.

Simple lesson: frontmatter holds the data, the Markdown below it holds the content.

Layouts for Markdown Pages

The layout Frontmatter Property

  • A bare Markdown page has no <html>, <head> or header.
  • Add layout: ../layouts/PostLayout.astro to the frontmatter.
  • Astro wraps the page in that layout.

Receiving frontmatter Props in a Layout

  • The layout gets the frontmatter as a prop called frontmatter.
  • It also gets headings: a list of every heading, with depth, slug and text.
  • The Markdown content arrives in the layout's <slot />.

Animation: the frontmatter goes to the layout as the frontmatter prop. The Markdown body becomes HTML and goes into the layout's slot.

first-post.md

title: My first post layout: PostLayout

# Heading Some bold text

PostLayout.astro frontmatter.title

<slot />

frontmatter prop HTML into the slot

Data goes in as a prop. The content goes into the slot.

Press ▶ Run. The layout shows the title, the author and a small table of contents from headings.

In Markdown pages, the layout field is the only way to add a layout. In content collections, you will render Markdown inside an .astro page instead. That is the more common way in real projects.

Simple lesson: layout: in frontmatter wraps a Markdown page. The layout reads frontmatter and headings.

Importing Markdown Files

import.meta.glob()

  • import.meta.glob() loads many files at once, using a glob pattern.
  • Glob pattern = a file path with wildcards. * means "any name". ** means "any folders".
  • import.meta.glob("../posts/*.md", { eager: true }) loads every .md file in src/posts.
  • It returns an object: each key is a file path, each value is the file's module.
  • A Markdown module has frontmatter, url, file, getHeadings() and a Content component.

Astro.glob() Was Removed

  • Older tutorials use Astro.glob(). It no longer exists in Astro 7.
  • import.meta.glob() is the replacement. It comes from Vite, the build tool inside Astro.
  • With { eager: true }, the files load right away. Without it, each value is a function you must call.

Press ▶ Run. The home page lists every post file in src/posts, without listing them by hand.

  • Add a third file in your head: it would appear in the list with no other change.
  • For real blogs, Content Collections do this job better. They also check each file's frontmatter.

Simple lesson: import.meta.glob() reads many files at once. It replaced Astro.glob().

Markdown Processing

The Sätteri Processor (Default in Astro 7)

  • Markdown processor = the tool that turns Markdown text into HTML.
  • Astro 7's default processor is Sätteri, Astro's own pipeline written in Rust. It is very fast.
  • It supports GitHub-Flavored Markdown (GFM): tables, task lists, strikethrough and footnotes.

The Unified Processor

  • Unified = the older processor, built from remark (Markdown) and rehype (HTML) plugins.
  • Use it when you need a remark or rehype plugin, such as remark-toc for a table of contents.

Switching Between Processors

You choose the processor with markdown.processor in astro.config.mjs:

import { defineConfig } from "astro/config";
import { unified } from "@astrojs/markdown-remark";
import remarkToc from "remark-toc";

export default defineConfig({
  markdown: {
    processor: unified({
      remarkPlugins: [remarkToc],
    }),
  },
});
  • To set options for Sätteri, install @astrojs/markdown-satteri and pass satteri({ ... }) the same way.

The markdown Configuration Option

  • markdown in the config holds all Markdown settings: the processor, syntaxHighlight and shikiConfig.
  • Older top-level options, such as markdown.remarkPlugins, still work but are deprecated. Pass plugins to the processor instead.

In this lab, Markdown is built by Astro's unified processor (@astrojs/markdown-remark), because Sätteri's browser build needs a feature this site does not turn on. For normal Markdown, both give the same HTML.

Simple lesson: Sätteri is the fast default. Switch to unified() when you need remark or rehype plugins.

Syntax Highlighting

Shiki Highlighting for Code Blocks

  • Syntax highlighting = colouring code so it is easier to read.
  • In Markdown, a code block starts with three backticks and a language name, such as js or bash.
  • Astro colours it at build time with Shiki, the default highlighter. No JavaScript is sent.

Themes

  • Shiki has many themes. Pick one in the config:
markdown: {
  shikiConfig: {
    theme: "github-dark",
  },
},
  • For light and dark mode, set two themes: themes: { light: "github-light", dark: "github-dark" }. Astro writes both as CSS variables. Your CSS picks one.
  • You can add your own theme as a JSON file, or change colours with transformers.

The Code Component

  • In .astro files, use <Code /> to highlight a string of code:
---
import { Code } from "astro:components";
---

<Code code={`const site = "Dev Journal";`} lang="js" theme="dracula" />
  • Its props: code, lang, theme, and transformers for extra changes.

Prism (Opt-In)

  • Prism = another highlighter. Turn it on with markdown: { syntaxHighlight: "prism" }.
  • Prism only adds class names. You add a Prism CSS theme yourself.
  • In .astro files, use <Prism lang="js" code={...} /> from @astrojs/prism.

Press ▶ Run. This lab highlights with Prism, so the code blocks get token classes. A small Prism theme is added with CSS.

Highlighting at build time means visitors download no highlighter code. The colours are just HTML and CSS.

Simple lesson: code blocks are coloured at build time. Shiki by default, Prism if you choose it.

MDX

Adding MDX with astro add mdx

  • MDX = Markdown that can also use components and JavaScript expressions.
  • Add it with npx astro add mdx. Then .mdx files work like .md files.

Using Components in MDX

---
title: My MDX post
---
import Note from "../components/Note.astro";

# Hello

<Note>This box is an Astro component inside Markdown.</Note>

Exporting Variables from MDX

  • An MDX file can export values: export const readingTime = 3;.
  • Use them in the same file with {readingTime}.

Replacing HTML Elements with Custom Components

  • <Content components={{ h2: FancyHeading }} /> swaps every h2 for your own component.
  • Useful to add an icon to every link, or a copy button to every code block.

Choosing Between Markdown and MDX

Use Markdown when:

  • the content is plain text, headings, lists, links and images,
  • writers who do not code will edit it.

Use MDX when:

  • the content needs components, such as notes, charts or demos,
  • you are comfortable with a little JSX syntax.

Markdoc (Alternative)

  • Markdoc = another format with its own tag syntax, {% note %}...{% /note %}.
  • Add it with npx astro add markdoc. Teams pick it when writers must not run any code.

MDX needs the @astrojs/mdx package. This lab cannot install packages, so try the MDX examples on your own computer.

Simple lesson: Markdown for plain content. MDX when content needs components.

TL;DR

  1. Markdown = formatted text from plain characters. .md in src/pages = a page.
  2. Frontmatter = YAML data between --- lines at the top.
  3. Heading IDs = made from the text: ## What I like → #what-i-like.
  4. layout: in frontmatter = wrap the page. The layout gets frontmatter, headings and a <slot />.
  5. import.meta.glob() = load many files: import.meta.glob("../posts/*.md", { eager: true }). It replaced Astro.glob().
  6. Processors:

    • Sätteri = Astro 7's fast default.
    • unified() = remark and rehype plugins.
  7. Highlighting = Shiki by default, Prism opt-in, <Code /> in .astro files.
  8. MDX = Markdown plus components. npx astro add mdx.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
Using Astro.glob()Old code still worksUse import.meta.glob()Build error in Astro 7
Wrong layout path in frontmatterPaths start from srcThe path is relative to the .md fileBuild error: layout not found
Forgetting { eager: true }Values are modulesWithout it, values are functions to callpost.frontmatter is undefined
Bad YAML indentationAny spacing worksUse key: value, one per line, with a space after the colonThe frontmatter is empty or the build fails
remarkPlugins with SätteriAll processors take remark pluginsUse unified() for remark pluginsThe plugin is ignored or errors
Components in a .md fileMarkdown runs componentsUse .mdx for componentsThe tag shows as plain text

Check Yourself

Exercises

What to Remember

  • A .md file in src/pages is a page. Frontmatter holds its data.
  • layout: wraps a Markdown page. The layout gets frontmatter and headings.
  • import.meta.glob() loads many files. Astro.glob() is gone.
  • Sätteri is the default processor. unified() runs remark and rehype plugins.
  • Code blocks are highlighted at build time. MDX adds components.

Next: Content Collections, where every post is checked by a schema before the site is built.