Markdown

Markdown content is commonly used to author text-heavy content like blog posts and documentation. Astro includes built-in support for Markdown with some added features like support for JavaScript expressions and Astro components right in your Markdown.

Astro treats any .md file inside of the /src/pages directory as a page. Placing a file in this directory, or any sub-directory, will automatically build a page route using the pathname of the file.

📚 Read more about Astro’s file-based routing.

The easiest way to start using Markdown in Astro is to create a src/pages/index.md homepage route in your project. Copy the basic template below into your project, and then view the rendered HTML at the homepage route of your project. Usually, this is at http://localhost:3000/.

---
# Example: src/pages/index.md
title: Hello, World
---

# Hi there!

This is your first markdown page. It probably isn't styled much, although
Markdown does support **bold** and _italics._

To learn more about adding a layout to your page, read the next section on **Markdown Layouts.**

Markdown pages have a special frontmatter property for layout that defines the relative path to an Astro layout component. This component will wrap your Markdown content, providing a page shell and any other included page template elements.

---
layout: ../layouts/BaseLayout.astro
---

A typical layout for Markdown pages includes:

  1. the content prop to access the Markdown page’s frontmatter data.
  2. a default <slot /> to indicate where the page’s Markdown content should be rendered.
---
// src/layouts/BaseLayout.astro
// 1. The content prop gives access to frontmatter data
const { content } = Astro.props;
---
<html>
  <head>
    <!-- Add other Head elements here, like styles and meta tags. -->
    <title>{content.title}</title>
  </head>
  <body>
    <!-- Add other UI components here, like common headers and footers. -->
    <h1>{content.title} by {content.author}</h1>
    <!-- 2. Rendered HTML will be passed into the default slot. -->
    <slot />
    <p>Written on: {content.date}</p>
  </body>
</html>

The content prop also contains an astro property with additional metadata about the page such as the complete Markdown source and a headers object.

An example blog post content object might look like:

{
  /** Frontmatter from a blog post
  "title": "Astro 0.18 Release",
  "date": "Tuesday, July 27 2021",
  "author": "Matthew Phillips",
  "description": "Astro 0.18 is our biggest release since Astro launch.",
  "draft": false,
  "keywords": ["astro", "release", "announcement"]
  **/
  "astro": {
    "headers": [
      {
        "depth": 1,
        "text": "Astro 0.18 Release",
        "slug": "astro-018-release"
      },
      {
        "depth": 2,
        "text": "Responsive partial hydration",
        "slug": "responsive-partial-hydration"
      }
      /* ... */
    ],
    "source": "# Astro 0.18 Release\nA little over a month ago, the first public beta [...]"
  },
  "url": ""
}

💡 astro and url are the only guaranteed properties provided by Astro in the content prop. The rest of the object is defined by your frontmatter variables.

Any Astro component (not just layouts!) can receive the values defined in your Markdown frontmatter as props. You can specify several types of data using YAML frontmatter, and capture even richer meta information from each blog post to use throughout your Astro site.

Access these values in any .astro file as you would in a layout, as described above.

Astro will add autogenerated ids to all headings in Markdown files automatically using github-slugger. But, if a custom id is specified, it won’t be overriden.

These ids will be added after all the other plugins are executed, so if you have a plugin like rehype-toc that needs ids, you should add your own slugging plugin (like rehype-slug).

draft: true is an optional frontmatter value that will mark an individual .md page or post as “unpublished.” By default, this page will be excluded from the site build.

Markdown pages without the draft property or those with draft: false are unaffected and will be included in the final build.

---
# src/pages/post/blog-post.md
layout: ../../layouts/BaseLayout.astro
title: My Blog Post
draft: true
---

This is my in-progress blog post.

No page will be built for this post.

To build and publish this post:

- update the frontmatter to `draft: false` or
- remove the `draft` property entirely.

⚠️ Although draft: true will prevent a page from being built on your site at that page route, Astro.glob() currently returns all your Markdown files.

To exclude the data (e.g. title, link, description) from a draft post from being included in your post archive, or list of most recent posts, be sure that your Astro.glob() function also filters to exclude any draft posts.

⚙️ To enable building draft pages:

Add drafts: true to markdown in astro.config.mjs

// astro.config.mjs
export default defineConfig({
  markdown: {
    drafts: true,
  },
});

💡 You can also pass the --drafts flag when running astro build to build draft pages!

In addition to supporting standard Markdown syntax, Astro also extends Markdown to make your content even more expressive. Below are some Markdown features that only exist in Astro.

frontmatter variables can be used directly in your Markdown as properties of the frontmatter object.

---
author: Leon
age: 42
---

# About the Author

{frontmatter.author} is {frontmatter.age} and lives in Toronto, Canada.

You can import components into your Markdown file with setup and use them alongside your Markdown content. The frontmatter object is also available to any imported components.

---
layout: ../layouts/BaseLayout.astro
setup: |
  import Author from '../../components/Author.astro'
  import Biography from '../components/Biography.jsx'
author: Leon
---

<Author name={frontmatter.author}/>
<Biography client:visible>
  {frontmatter.author} lives in Toronto, Canada and enjoys photography.
</Biography>

You can import Markdown files directly into your Astro files! You can import one specific page with import or multiple with Astro.glob()

---
// Import some markdown. Dynamic import() is also supported!
import * as greatPost from '../pages/post/great-post.md';

// Also, you can import multiple files with Astro.glob
const posts = await Astro.glob('../pages/post/*.md');
---

Great post: <a href={greatPost.url}>{greatPost.frontmatter.title}</a>

<ul>
  {posts.map(post => <li>{post.frontmatter.title}</li>)}
</ul>

Each Markdown file exports the following properties:

  • frontmatter: Any data specified in this file’s YAML frontmatter.

  • file: The absolute path of this file (e.g. /home/user/projects/.../file.md).

  • url: If it’s a page, URL of the page (e.g. /en/guides/markdown-content).

  • getHeaders(): An async function that returns the headers of the Markdown file. The response follows this type: { depth: number; slug: string; text: string }[].

  • Content: A component that renders the contents of the Markdown file. Here is an example:

    ---
    import {Content as PromoBanner} from '../components/promoBanner.md';
    ---
    
    <h2>Today's promo</h2>
    <PromoBanner />

You can optionally provide a type for the frontmatter variable using a TypeScript generic:

---
interface Frontmatter {
  title: string;
  description?: string;
}
const posts = await Astro.glob<Frontmatter>('../pages/post/*.md');
---

<ul>
  {posts.map(post => <li>{post.title}</li>)}
  <!-- post.title will be `string`! -->
</ul>

NOTE: The <Markdown /> component does not work in SSR and may be removed before v1.0. It should should be avoided if possible. To use Markdown in your templates, use a separate .md file and then import Markdown into your template as a component.

You can import the built-in Astro Markdown component in your component script and then write any Markdown you want between <Markdown></Markdown> tags.

---
import { Markdown } from 'astro/components';
import Layout from '../layouts/Layout.astro';

const expressions = 'Lorem ipsum';
---
<Layout>
  <Markdown>
    # Hello world!

    **Everything** supported in a `.md` file is also supported here!

    There is _zero_ runtime overhead.

    In addition, Astro supports:
    - Astro {expressions}
    - Automatic indentation normalization
    - Automatic escaping of expressions inside code blocks

    ```js
      // This content is not transformed!
      const object = { someOtherValue };
    ```

    - Rich component support like any `.astro` file!
    - Recursive Markdown support (Component children are also processed as Markdown)
  </Markdown>
</Layout>

NOTE: The <Markdown /> component does not work in SSR and may be removed before v1.0. It should should be avoided if possible. To use Markdown in your templates, use a separate .md file and then import it into your template as a component. Read this RFC Discussion to learn more.

If you have Markdown in a remote source, you may pass it directly to the Markdown component through the content attribute.

---
import { Markdown } from 'astro/components';

const content = await fetch('https://raw.githubusercontent.com/withastro/docs/main/README.md').then(res => res.text());
---
<Layout>
  <Markdown content={content} />
</Layout>

NOTE: The <Markdown /> component does not work in SSR and may be removed before v1.0. It should should be avoided if possible. To use Markdown in your templates, use a separate .md file and then import it into your template as a component. Read this RFC Discussion to learn more.

<Markdown /> components can be nested.

---
import { Markdown } from 'astro/components';

const content = await fetch('https://raw.githubusercontent.com/withastro/docs/main/README.md').then(res => res.text());
---

<Layout>
  <Markdown>
    ## Markdown example

    Here we have some __Markdown__ code. We can also dynamically render remote content.

    <Markdown content={content} />
  </Markdown>
</Layout>

⚠️ Use of the Markdown component to render remote Markdown can open you up to a cross-site scripting (XSS) attack. If you are rendering untrusted content, be sure to sanitize your content before rendering it.

You can customize your Markdown parsing by modifing your astro.config.mjs. Here you can read the full reference.

Astro supports third-party remark and rehype plugins for Markdown. You can provide your plugins in astro.config.mjs.

Note: By default, Astro comes with GitHub-flavored Markdown and remark-smartypants pre-enabled. Enabling custom remarkPlugins or rehypePlugins will remove these built-in plugins and you need to explicitly add these plugins if desired.

How to add a Markdown plugin in Astro

Section titled How to add a Markdown plugin in Astro
  1. Install the npm package dependency in your project.

  2. Update remarkPlugins or rehypePlugins inside the markdown options:

    // astro.config.mjs
    export default {
      markdown: {
        remarkPlugins: [
          // Add a Remark plugin that you want to enable for your project.
          // If you need to provide options for the plugin, you can use an array and put the options as the second item.
          // ['remark-autolink-headings', { behavior: 'prepend'}],
        ],
        rehypePlugins: [
          // Add a Rehype plugin that you want to enable for your project.
          // If you need to provide options for the plugin, you can use an array and put the options as the second item.
          // 'rehype-slug',
          // ['rehype-autolink-headings', { behavior: 'prepend'}],
        ],
      },
    };

    You can provide names of the plugins as well as import them:

    // astro.config.mjs
    import autolinkHeadings from 'remark-autolink-headings';
    
    export default {
      markdown: {
        remarkPlugins: [[autolinkHeadings, { behavior: 'prepend' }]],
      },
    };

Astro comes with built-in support for Shiki and Prism. This provides instant syntax highlighting for:

Shiki is enabled by default, preconfigured with the github-dark theme. The compiled output will be limited to inline styles without any extraneous CSS classes, stylesheets, or client-side JS.

If you opt to use Prism, we will apply Prism’s CSS classes instead. Note that you need to bring your own CSS stylesheet for syntax highlighting to appear! See the Prism configuration section for more details.

Shiki is our default syntax highlighter. If you’d like to switch to 'prism' or disable syntax highlighting entirely, you can use the markdown config object:

// astro.config.mjs
export default {
  markdown: {
    // Can be 'shiki' (default), 'prism' or false to disable highlighting
    syntaxHighlight: 'prism',
  },
};

When using Shiki, you’ll configure all options via the shikiConfig object like so:

// astro.config.mjs
export default {
  markdown: {
    shikiConfig: {
      // Choose from Shiki's built-in themes (or add your own)
      // https://github.com/shikijs/shiki/blob/main/docs/themes.md
      theme: 'dracula',
      // Add custom languages
      // Note: Shiki has countless langs built-in, including .astro!
      // https://github.com/shikijs/shiki/blob/main/docs/languages.md
      langs: [],
      // Enable word wrap to prevent horizontal scrolling
      wrap: true,
    },
  },
};

We also suggest diving into their theme documentation to explore loading custom theme, light vs dark mode toggles, or styling via CSS variables.

When using Prism, you’ll need to add a stylesheet to your project for syntax highlighting. If you’re just getting started and prefer to use Prism over Shiki, we suggest:

  1. Setting syntaxHighlight: 'prism' from your @astrojs/markdown-remark config.
  2. Choosing a premade stylesheet from the available Prism Themes.
  3. Adding this stylesheet to your project’s public/ directory.
  4. Loading this into your page’s <head> via a <link> tag.

You can also visit the list of languages supported by Prism for options and usage.