---
title: "Astro: MDX Integration"
slug: "astro-mdx"
category: "frontend"
tags: ["frontend", "astro", "mdx", "markdown", "components"]
status: "stable"
last_updated: 2026-05-14
summary: "Embed components in markdown with MDX, wire up remark and rehype plugins, and avoid the perf traps of per-file component imports."
related: ["[[frontend/astro]]", "[[frontend/astro-content-collections]]", "[[frontend/astro-integrations]]", "[[frontend/astro-performance]]", "[[comparisons/nextjs-vs-astro]]", "[[frontend/react]]"]
---

## Overview

MDX lets you use JSX components inside markdown files. In Astro, the `@astrojs/mdx` integration enables `.mdx` files in `src/pages/` and inside Content Collections. Use MDX when a content page needs interactive islands, custom callout components, or embedded data visualizations that plain markdown cannot express. For pages that are entirely prose, prefer `.md` over `.mdx`; MDX adds a build-time transform cost and ships a slightly larger output.

## Install the integration with `astro add`

```bash
npx astro add mdx
```

This installs `@astrojs/mdx` and adds it to `astro.config.mjs` automatically. No further config is needed for basic MDX support. See [[frontend/astro-integrations]] for the full `astro add` workflow.

## Import components directly in an MDX file

Components used in one MDX file go at the top of that file. They are local imports, not global.

```mdx
---
title: "My Post"
---
import { Chart } from "../components/Chart";
import Callout from "../components/Callout.astro";

<Callout type="warning">Read this first.</Callout>

The data tells an interesting story.

<Chart data={[1, 2, 3]} />
```

Astro components (`.astro`) and framework components both work in MDX. Add `client:*` directives to framework components the same way you would in a `.astro` file.

## Use `components` prop to replace HTML elements globally

Pass a `components` object to an MDX layout to override HTML elements site-wide. This replaces every `<h2>`, `<a>`, or `<img>` in MDX output with a custom component without touching individual files.

```astro
---
// src/layouts/BlogLayout.astro
import { MDXProvider } from "@astrojs/mdx";
import Heading from "../components/Heading.astro";
import ExternalLink from "../components/ExternalLink.astro";

const components = { h2: Heading, a: ExternalLink };
---
<slot components={components} />
```

Use this for syntax-highlighted code blocks, custom image wrappers, and anchor tags that add external link icons. The components object flows through the layout and applies to all MDX content rendered by that layout.

## Add remark and rehype plugins in `astro.config.mjs`

Remark transforms the markdown AST before MDX processes it; rehype transforms the HTML AST after. Configure plugins in `astro.config.mjs`.

```js
// astro.config.mjs
import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import remarkGfm from "remark-gfm";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeSlug from "rehype-slug";

export default defineConfig({
  integrations: [
    mdx({
      remarkPlugins: [remarkGfm],
      rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings],
    }),
  ],
});
```

Common plugin picks: `remark-gfm` for GitHub Flavored Markdown (tables, task lists), `rehype-slug` to add IDs to headings, `rehype-autolink-headings` for anchor links, and `rehype-pretty-code` or `shiki` for syntax highlighting.

## Export named values from MDX for metadata access

MDX files can export named constants. Use this to expose structured data to the consuming page without duplicating it in frontmatter.

```mdx
export const toc = [
  { title: "Overview", slug: "overview" },
  { title: "Rules", slug: "rules" },
];
```

Access the export in a `.astro` file that imports the MDX module: `const { toc } = await import("./my-post.mdx")`. This is useful for generating a table of contents without parsing headings in a separate step.

## Avoid importing MDX into other MDX files

Importing one MDX file into another creates a dependency graph that Vite re-transforms on every change to either file. On large sites this slows incremental builds noticeably. Compose shared MDX content via Astro components or Content Collection queries instead.

Also avoid heavy client-side framework components in MDX unless they need interactivity. A React component without a `client:*` directive renders to static HTML at build time, which is fine. A component with `client:load` in an MDX file ships its framework runtime to every page that includes that MDX file.

## Related

- [[frontend/astro]]
- [[frontend/astro-content-collections]]
- [[frontend/astro-integrations]]
- [[frontend/astro-performance]]
- [[comparisons/nextjs-vs-astro]]
- [[frontend/react]]
