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.
# Titlemakes a heading.**word**makes bold text.- itemmakes a list.- Writers like it because they can focus on words, not tags.
- Astro turns
.mdfiles into HTML pages for you. - In this lesson, Asha moves her posts from
.astrocode 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
.mdfile insrc/pagesbecomes a page, like an.astrofile.
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 asid="what-i-like".
What You Will Learn
In this lesson, you will learn:
- How Markdown pages and frontmatter work.
- How headings get IDs for anchor links.
- How to read many Markdown files with
import.meta.glob(). - How to give Markdown pages a layout.
- How Astro processes Markdown, and how to change the processor.
- How code blocks get syntax highlighting.
- What MDX adds, and when to use it.
Markdown Basics
Markdown Pages
- A
.mdfile insrc/pagesbecomes a page.src/pages/first-post.mdbuilds to/first-post. - The same routing rules apply as for
.astrofiles.
Frontmatter
- Frontmatter = data at the top of a Markdown file, between two
---lines. - It is written in YAML:
key: valueon 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
idmade from its text. ## What I likegetsid="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 guideloads the Markdown file as a module.guide.frontmatterholds its data.## Install Node.jsgotid="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.astroto 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, withdepth,slugandtext. - 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.mdfile insrc/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 aContentcomponent.
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-tocfor 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-satteriand passsatteri({ ... })the same way.
The markdown Configuration Option
markdownin the config holds all Markdown settings: the processor,syntaxHighlightandshikiConfig.- 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
jsorbash. - 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
.astrofiles, 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, andtransformersfor 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
.astrofiles, 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.mdxfiles work like.mdfiles.
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 everyh2for 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
- Markdown = formatted text from plain characters.
.mdinsrc/pages= a page. - Frontmatter = YAML data between
---lines at the top. - Heading IDs = made from the text:
## What I like→#what-i-like. - layout: in frontmatter = wrap the page. The layout gets
frontmatter,headingsand a<slot />. - import.meta.glob() = load many files:
import.meta.glob("../posts/*.md", { eager: true }). It replacedAstro.glob(). Processors:
- Sätteri = Astro 7's fast default.
unified()= remark and rehype plugins.
- Highlighting = Shiki by default, Prism opt-in,
<Code />in.astrofiles. - MDX = Markdown plus components.
npx astro add mdx.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
Using Astro.glob() | Old code still works | Use import.meta.glob() | Build error in Astro 7 |
| Wrong layout path in frontmatter | Paths start from src | The path is relative to the .md file | Build error: layout not found |
Forgetting { eager: true } | Values are modules | Without it, values are functions to call | post.frontmatter is undefined |
| Bad YAML indentation | Any spacing works | Use key: value, one per line, with a space after the colon | The frontmatter is empty or the build fails |
remarkPlugins with Sätteri | All processors take remark plugins | Use unified() for remark plugins | The plugin is ignored or errors |
Components in a .md file | Markdown runs components | Use .mdx for components | The tag shows as plain text |
Check Yourself
Exercises
What to Remember
- A
.mdfile insrc/pagesis a page. Frontmatter holds its data. layout:wraps a Markdown page. The layout getsfrontmatterandheadings.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.