TL;DR: MDX is Markdown plus JSX. You write normal Markdown prose — headings, paragraphs, lists — and you can drop React components directly into the content wherever you need interactive or styled UI. AI generates .mdx files automatically for Next.js blogs, Docusaurus documentation sites, and Astro content sites. You do not need to fully understand React to write content in MDX, but you need to understand it to build or fix the components inside it.
Why AI Coders Need to Know This
If you have ever asked AI to build a blog, a documentation site, or a landing page with a content management layer, you have almost certainly received .mdx files back. They look like Markdown at first glance — and most of the content is Markdown — but there are component tags mixed in that look like HTML but are not quite HTML. And the file ends in .mdx instead of .md.
If you do not know what MDX is, those files are confusing in a specific way: they are 90% familiar and 10% alien. The alien 10% is JSX — the syntax that React uses to describe UI. Once you understand what MDX is doing, that 10% stops being mysterious and becomes genuinely useful.
More practically: MDX is the default content format for three of the most popular AI-generated project stacks. When you prompt for a Next.js blog, the AI uses MDX. When you ask for a documentation site with Docusaurus, the AI uses MDX. When you use Astro, the default content format is MDX. If you work with any of these frameworks, you will encounter MDX files constantly. Understanding them is not optional.
This article explains what MDX does, how to read the syntax AI generates, and when MDX makes sense versus when plain Markdown or plain React is the better choice.
Real Scenario: You Ask for a Blog, You Get .mdx Files
Chuck wants a developer blog. He opens Cursor and types:
Prompt I Would Type
Build me a personal developer blog with Next.js. I want:
- A home page listing all my posts
- Individual post pages with nice typography
- The ability to embed code blocks with syntax highlighting
- A way to add a newsletter signup form or callout box mid-post
- Posts written in Markdown so I can write them easily
Use the App Router. Make it look clean and minimal.
Cursor does not hand Chuck plain .md files. It hands him something like this:
my-blog/
app/
page.tsx ← home page (lists all posts)
blog/
[slug]/
page.tsx ← individual post page
content/
posts/
hello-world.mdx ← first post (MDX format)
my-first-project.mdx
components/
Callout.tsx ← a component used inside posts
NewsletterSignup.tsx ← another component used inside posts
CodeBlock.tsx ← syntax-highlighted code component
lib/
mdx.ts ← utility that reads and processes MDX files
The content/posts/ folder holds Chuck's actual blog posts. Each one is an .mdx file. The components/ folder holds the React components that his posts can use inline. The lib/mdx.ts file is the plumbing that reads the MDX files and turns them into rendered pages.
This is the standard AI-generated MDX blog structure. Once you have seen it once, you recognize it everywhere. Let us look at what is actually inside one of those .mdx files.
What AI Generated: A Real .mdx File
Here is a realistic example of an .mdx blog post as AI would generate it. Read the comments — they explain exactly what each part is doing.
---
title: "Why I Switched to Using AI for Every Side Project"
date: "2026-03-15"
description: "Three months in, here is what changed."
author: "Chuck"
tags: ["ai", "productivity", "side-projects"]
---
{/* This block at the top is called frontmatter — YAML metadata */}
{/* The dashes (---) mark where it starts and ends */}
{/* This data gets read by the blog engine, not shown to the reader */}
import Callout from '@/components/Callout'
import NewsletterSignup from '@/components/NewsletterSignup'
{/* These two lines import React components from the components folder */}
{/* You can then use them anywhere in this file like HTML tags */}
## The First Month Was Humbling
I started using Claude Code for everything in January. The first week I felt
like I was cheating. The second week I felt like I was drowning in code I
didn't understand. By the end of the month I had shipped three projects.
This is just normal Markdown. Paragraphs, headings, lists — all the usual stuff.
No React, no JSX, just text.
<Callout type="tip">
If you are new to AI coding tools, start with a project you already
understand. The AI accelerates you — it does not replace the need to
know your domain.
</Callout>
{/* ^^^ That is a React component embedded directly in the Markdown */}
{/* It looks like an HTML tag but it is JSX — it calls the Callout component */}
{/* The content between the tags becomes the children prop */}
## What Actually Changed
The biggest shift was not speed. It was **confidence**. I started projects
I would have given up on before. Here is why:
- I could ask "why is this broken" and get an answer in seconds
- I stopped Googling the same Stack Overflow answers every week
- I shipped things that felt too hard to attempt alone
```javascript
// AI even generates my code examples with syntax highlighting
// This is a standard Markdown code fence — MDX supports them natively
const result = await fetch('/api/posts')
const posts = await result.json()
```
## Want More Like This?
<NewsletterSignup
headline="Get weekly AI coding tips"
subtext="No spam. Unsubscribe any time."
/>
{/* Self-closing component tag — no children content, just props */}
That file is a complete blog post. The prose sections are pure Markdown — Chuck writes them exactly the way he would write any Markdown file. The <Callout> and <NewsletterSignup> tags are React components that got imported at the top. When the Next.js build processes this file, it turns the Markdown into HTML and renders the components in place.
The result is a fully styled blog post where the Callout box is a proper React component — interactive if it needs to be, consistently styled, and reusable across every post — while the prose is just text Chuck typed.
Understanding MDX Syntax
MDX has five building blocks. Every .mdx file AI generates will use some combination of them.
1. Frontmatter: the metadata block at the top
Almost every AI-generated MDX file starts with a frontmatter block — a section between triple dashes that holds structured metadata about the page.
---
title: "My Post Title"
date: "2026-03-15"
description: "A short description for SEO and social sharing"
tags: ["react", "mdx", "tutorial"]
published: true
---
Frontmatter is not MDX-specific — it comes from Markdown. But every MDX blog and docs framework reads it. The title powers your page's <title> tag. The description becomes your meta description. The date sorts your posts list. None of this content appears on the rendered page — it is data for the framework to use.
If AI generates frontmatter that seems excessive, it is because the blog template it was building against uses all those fields. You can delete fields you do not need.
2. Markdown: the prose you already know
Everything that is not a component, not an import, and not frontmatter is regular Markdown. It works exactly as you expect.
## A Heading
A normal paragraph with **bold**, *italic*, and a [link](https://example.com).
- Bullet one
- Bullet two
- Bullet three
```javascript
// A fenced code block with syntax highlighting
const x = 42
```
If you have ever written a GitHub README, a Notion doc, or a Discord message with formatting, you have written Markdown. MDX does not change anything about the Markdown portion of your file.
3. Imports: bringing components into the file
Before you can use a React component in an MDX file, you import it at the top — exactly like you would in a React file.
import Callout from '@/components/Callout'
import { Chart, DataTable } from '@/components/DataViz'
import NewsletterSignup from '@/components/NewsletterSignup'
The @/ is a path alias that means "the root of the project." AI sets this up in the Next.js config automatically. After the import, the component is available to use anywhere below it in the file.
4. JSX component tags: using components inline
Once imported, you use a component the same way you use an HTML tag — but with a capital letter, because React uses capital letters to distinguish components from HTML elements.
{/* Component with children — content between the tags */}
<Callout type="warning">
This is a **warning** — you can still use Markdown formatting inside!
</Callout>
{/* Self-closing component — just props, no children */}
<NewsletterSignup headline="Get updates" buttonText="Subscribe" />
{/* Component with complex children */}
<CodePlayground language="javascript">
const greeting = "Hello, MDX"
console.log(greeting)
</CodePlayground>
The attributes on these tags are props — data you are passing into the component. type="warning", headline="Get updates", and language="javascript" are all props. The component decides what to do with them. If you need to understand how components read props, the What Is a Component? article covers this in detail.
5. JSX expressions: JavaScript inline in the content
You can also drop JavaScript expressions directly into MDX using curly braces. This is less common in content files, but AI sometimes generates it.
export const publishDate = new Date('2026-03-15')
This post was published on {publishDate.toLocaleDateString('en-US', {
month: 'long', day: 'numeric', year: 'numeric'
})}.
{/* Comments in MDX use JSX comment syntax, not HTML comments */}
{/* <!-- HTML comments do NOT work in MDX --> */}
The curly brace syntax evaluates the JavaScript expression and inserts the result into the rendered output. It is the same syntax React uses in JSX. One important note: HTML comments (<!-- like this -->) do not work in MDX — you use the JSX comment syntax instead ({/* like this */}). AI occasionally forgets this and generates HTML comments in MDX files, which causes build errors.
MDX vs Regular Markdown vs HTML
The three formats overlap and it is easy to reach for the wrong one. Here is the practical breakdown.
Use plain Markdown (.md) when:
- You just want to write text with headings and links
- The content never needs interactive or styled components
- You are writing for GitHub, Notion, or a simple static site
- Speed of writing matters more than UI flexibility
- You want the file to be readable in any Markdown viewer
Use MDX (.mdx) when:
- Your content needs embedded components (callouts, forms, charts)
- You are using Next.js, Docusaurus, or Astro
- You want consistent styled elements across many posts
- The same component appears in multiple content files
- AI is building the whole stack anyway — .mdx is its default
What about plain HTML? HTML is what MDX eventually becomes after the build process runs. You do not write HTML in MDX — you write Markdown and components, and the framework converts both into HTML automatically. If you find yourself writing raw HTML tags inside an MDX file, that is usually a sign either that a component should exist for that UI pattern, or that you are fighting the format.
The simple rule
If you are on a Next.js, Docusaurus, or Astro project, use .mdx by default — even if you never add a component. Switching from .md to .mdx later requires renaming files and updating imports. Starting with .mdx costs nothing and keeps your options open.
Where You Will See MDX
MDX is not universal — it shows up in specific framework contexts. Here is exactly where and how it is used in each one.
Next.js blogs and documentation
Next.js does not ship with MDX support built in, but AI always adds it. The typical setup uses a package called @next/mdx or a content layer library like Contentlayer or Velite. Your .mdx files live in a content/ folder, and the Next.js page that renders each post reads the file, processes it, and renders it as a React page.
// app/blog/[slug]/page.tsx — simplified version of what AI generates
import { getPostBySlug } from '@/lib/mdx'
export default async function BlogPost({ params }) {
const post = await getPostBySlug(params.slug)
// post.content is the rendered MDX — components and all
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}
The MDX file is just the content. The Next.js page is the frame around it. This separation is why the stack works: a designer can style the frame component, and a writer can add content to the .mdx file, and neither needs to touch the other's work.
Docusaurus documentation sites
Docusaurus is Facebook's open-source documentation framework, and it is MDX-first by default. Every page in a Docusaurus site is an .mdx file. The framework provides its own component library — Tabs, CodeBlock, Admonition — that documentation writers use constantly.
---
sidebar_position: 1
title: Getting Started
---
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
## Installation
<Tabs>
<TabItem value="npm" label="npm" default>
```bash
npm install my-package
```
</TabItem>
<TabItem value="yarn" label="yarn">
```bash
yarn add my-package
```
</TabItem>
</Tabs>
When you ask AI to build documentation for a library or project, it will generate exactly this kind of file. The Tabs component showing installation instructions in multiple package managers is the most common Docusaurus pattern in existence. Every major open-source project uses it.
Astro content sites
Astro uses a content collections system where your .mdx files live in src/content/ folders. Astro is notable because it can mix components from any framework — React, Vue, Svelte — in the same MDX file. This makes Astro MDX files especially likely to contain component imports when AI generates them.
---
# src/content/blog/my-post.mdx
title: "My Post"
pubDate: 2026-03-15
description: "A post about something interesting"
---
import ReactCounter from '../../components/ReactCounter.jsx'
import VueWidget from '../../components/VueWidget.vue'
## Two Frameworks, One File
Astro lets you mix component frameworks. Here is a React counter:
<ReactCounter client:load />
And here is a Vue widget:
<VueWidget client:visible />
The client:load and client:visible directives are Astro-specific — they control when the component's JavaScript loads in the browser. These are not standard MDX or React syntax. If you see them and wonder what they are, they are Astro's way of handling interactivity, not something MDX defines itself.
What AI Gets Wrong About MDX
AI generates MDX fluently but makes specific, repeatable mistakes. Knowing them before you hit them saves significant debugging time.
HTML comments inside MDX files
This is the single most common AI mistake with MDX. AI occasionally writes HTML-style comments inside .mdx files:
<!-- This is a comment -- AI does this sometimes -->
<Callout type="info">
Some content here
</Callout>
MDX does not support HTML comments. They cause a build error. The correct comment syntax in MDX is the JSX comment inside curly braces:
{/* This is the correct comment syntax in MDX */}
<Callout type="info">
Some content here
</Callout>
If your MDX build fails with a cryptic parse error, look for <!-- --> comments first.
Components that are never imported
AI sometimes writes component tags in an MDX file without adding the corresponding import at the top:
{/* AI forgot to add: import Callout from '@/components/Callout' */}
<Callout type="warning">
This will cause a build error because Callout is not imported.
</Callout>
The fix is straightforward — add the import at the top of the file. But finding the error can be confusing if you do not know what to look for. The build error will say something like "Callout is not defined" rather than "you forgot an import."
Mismatched prop names between the MDX file and the component
AI generates the .mdx content and the React component separately. Sometimes the prop names do not match:
{/* In the MDX file: */}
<Callout variant="warning">Watch out!</Callout>
{/* But in Callout.tsx, the prop is called "type" not "variant": */}
function Callout({ type, children }) {
return <div className={`callout callout--${type}`}>{children}</div>
}
The component renders but the styling breaks — the variant prop is passed but the component reads type, so it receives undefined and the class name becomes callout--undefined. Check the component definition whenever a component renders without the expected styling.
Mixing Markdown and JSX in ways that break parsing
MDX has specific rules about blank lines between Markdown content and JSX. If you put Markdown immediately adjacent to a component tag with no blank line, the parser sometimes gets confused:
{/* Can cause parsing issues: */}
Some text right here
<Callout>Content</Callout>
More text right here
{/* Better — blank lines around JSX blocks: */}
Some text right here
<Callout>Content</Callout>
More text right here
This is not a hard rule — the MDX parser handles many mixed cases correctly — but when you see strange output or parse errors with no obvious cause, adding blank lines around your component tags is the first thing to try.
Using the wrong version of MDX syntax
MDX went through a major version change between MDX 1/2 and MDX 3. Some older AI training data uses syntax from MDX 1 that no longer works. The most visible difference is that MDX 3 does not allow certain JavaScript expression patterns that MDX 1 accepted. If a project is on MDX 3 (which it will be in any 2025 or 2026 AI-generated project) and the AI generates MDX 1 syntax, the build will fail. Ask the AI to regenerate using MDX 3 syntax.
Quick Debug Checklist for MDX Build Errors
When an MDX file fails to build: first check for HTML comments (<!-- -->) and replace them with {/* */}. Then check that every component used in the file is imported at the top. Then check that prop names in the MDX file match the prop names in the component definition. Finally, try adding blank lines around component tags.
What to Learn Next
MDX sits at the intersection of Markdown and React. To get the most out of it — or to fix it when it breaks — these are the foundations that matter most:
- What Is a Component? — the React building block you embed in MDX files. Understanding props, children, and composition makes MDX syntax fully readable.
- What Is React? — MDX compiles to React under the hood. Knowing React basics helps you debug MDX component issues confidently.
- What Is Next.js? — the framework where you will most often encounter MDX in AI-generated projects.
- What Is Astro? — if you want a content-heavy site with excellent performance, Astro with MDX is the stack AI reaches for most.
- Build a Blog With AI — a complete walkthrough of prompting AI to build a Next.js MDX blog from scratch, with the exact prompts and file structure explained step by step.
Frequently Asked Questions
MDX is a file format that combines Markdown (the plain-text writing format used by README files and wikis) with JSX (the HTML-like syntax used in React). In an MDX file you can write normal prose, headings, and bullet points just like Markdown, and then drop in a React component anywhere you need something interactive or styled. It is mostly used for blogs, documentation sites, and content-heavy pages where the writing and the UI need to coexist.
To write MDX content — just the prose parts — no. You write it exactly like Markdown. But to create or customize the components you drop into that content, you need to understand React components and JSX. If AI is generating both the MDX files and the components, you can often get a blog or docs site running without touching React at all. But when something breaks or you want to customize a component, that is when React knowledge becomes important.
A .md file is pure Markdown — text formatting, headings, links, images, and code blocks. Nothing interactive, no custom components. A .mdx file is Markdown plus the ability to import and use React components inline. If your content never needs interactive elements or custom UI, .md is simpler and faster. If you want a code playground, a newsletter signup box, or a comparison table component embedded mid-article, that is what .mdx is for.
Because the most popular AI-project stacks — Next.js with a content layer, Docusaurus, and Astro — all use MDX as their default content format. AI tools are trained on thousands of open-source repos using these frameworks, so when you ask for a Next.js blog or a documentation site, the AI defaults to the same patterns it has seen work. MDX is also genuinely useful: it lets content and UI live in the same file rather than splitting them across separate systems.
Yes — that is the whole point. You write normal Markdown for the prose (paragraphs, headings, lists, links) and drop in a React component only where you need one. Most of an MDX file looks exactly like a Markdown file. The components appear inline, clearly separate from the prose. A good MDX file might be 95% plain Markdown and 5% component tags.