Nuxt Content MDC Authoring Guide
MDC (Markdown Components) extends standard Markdown with Vue component support. Use this guide when authoring .md files for Nuxt Content.
Quick Reference
| Syntax | Description |
|---|---|
::component | Block component |
:component | Inline component |
{prop="value"} | Props |
#slotname | Named slot |
[text]{.class} | Span with attributes |
{{ $doc.var }} | Variable binding |
Frontmatter
YAML metadata block at the top of the file:
--- title: My Article description: A brief description author: John Doe date: 2024-01-15 tags: - tutorial - nuxt draft: false ---
Access frontmatter values in content using {{ $doc.propertyName }}.
Block Components
Block components use :: syntax and can contain Markdown content:
::alert{type="warning"}
This is a warning message with **Markdown** support.
::
Nested Components
::card ::card-header Card Title :: Card body content here. ::card-footer Footer text :: ::
Named Slots
Use #slotname to define named slots:
::card Default slot content goes here. #header This goes in the header slot. #footer This goes in the footer slot. ::
Self-Closing Block Components
For components without content:
::divider ::
Inline Components
Inline components use single : and flow with text:
Here is an :icon{name="heroicons:star"} icon inline.
Status: :badge[Active]{color="success"}
Click :button[Submit]{@click="handleSubmit"} to continue.
Inline Component with Content
Use square brackets for default slot content:
:badge[Premium]
:button[Click Me]{variant="outline"}
Props
Inline Props
::alert{type="info" icon="heroicons:information-circle"}
Content here
::
:icon{name="heroicons:check" class="text-green-500" size="24"}
YAML Props Block
For complex props, use a YAML block after the component declaration:
::card --- title: My Card image: /images/hero.jpg tags: - featured - new --- Card content with complex props defined above. ::
JSON Props (Arrays/Objects)
Prefix with : for JavaScript expressions:
::dropdown{:items='["Option A", "Option B", "Option C"]'}
::
::chart{:data='{"labels": ["Jan", "Feb"], "values": [10, 20]}'}
::
Boolean Props
::modal{closable} <!-- true -->
::modal{:closable="false"} <!-- explicit false -->
Dynamic Props
Bind to frontmatter variables:
---
cardTitle: Welcome
---
::card{:title="$doc.cardTitle"}
::
Slots
Default Slot
Content directly inside the component:
::callout This is the default slot content. It supports **Markdown** formatting. ::
Named Slots
::hero
#title
Welcome to Our Site
#subtitle
Build amazing things with Nuxt
#actions
:button[Get Started]{to="/docs"}
:button[Learn More]{to="/about" variant="outline"}
::
Nested Slots
::tabs
::tab{label="Preview"}
Preview content here.
::
::tab{label="Code"}
```ts
const example = 'code'
:: ::
## Spans & Attributes
Apply attributes to inline text using `[text]{attributes}`:
```markdown
This is [highlighted text]{.text-primary}.
[Custom styled]{.font-bold .text-lg #my-id style="color: red"}
[Link with class](/about){.nav-link}
Attributes on Standard Markdown
**bold text**{.text-red-500}
*italic*{.text-sm}
`inline code`{lang="ts"}
{width="300" loading="lazy"}
Multiple Classes
[styled text]{.class-one .class-two .class-three}
Variable Binding
Interpolate frontmatter values in content:
---
author: Jane Smith
publishDate: 2024-01-15
version: 2.0.0
---
# {{ $doc.title }}
Written by {{ $doc.author }} on {{ $doc.publishDate }}.
Current version: **{{ $doc.version }}**
Code Blocks
Basic Syntax Highlighting
```typescript const greeting: string = 'Hello, World!' console.log(greeting)
### Filename Display
```markdown
```ts [utils/helpers.ts]
export function formatDate(date: Date): string {
return date.toLocaleDateString()
}
### Line Highlighting
```markdown
```ts {2-4,6}
function example() {
// Lines 2-4 highlighted
const a = 1
const b = 2
// Line 5 not highlighted
return a + b // Line 6 highlighted
}
### Meta String
Combine filename and highlighting:
```markdown
```vue [components/Button.vue] {3-5}
<template>
<button
class="btn"
:class="variant"
@click="$emit('click')"
>
<slot />
</button>
</template>
### Code Groups ```markdown ::code-group ```bash [npm] npm install @nuxt/content
pnpm add @nuxt/content
yarn add @nuxt/content
::
## Prose Components Customize how standard Markdown elements render by creating components in `components/content/`: | Element | Component | Description | |---------|-----------|-------------| | `<p>` | `ProseP` | Paragraphs | | `<h1>` | `ProseH1` | Heading 1 | | `<h2>` | `ProseH2` | Heading 2 | | `<h3>` | `ProseH3` | Heading 3 | | `<a>` | `ProseA` | Links | | `<code>` | `ProseCode` | Inline code | | `<pre>` | `ProsePre` | Code blocks | | `<ul>` | `ProseUl` | Unordered lists | | `<ol>` | `ProseOl` | Ordered lists | | `<li>` | `ProseLi` | List items | | `<blockquote>` | `ProseBlockquote` | Blockquotes | | `<img>` | `ProseImg` | Images | | `<table>` | `ProseTable` | Tables | ## Excerpts Use `<!--more-->` to define excerpt boundaries: ```markdown --- title: My Article --- This is the excerpt that appears in listings. <!--more--> This is the full article content that only shows on the detail page.
Practical Component Examples
Alert/Callout
::alert{type="info"}
This is an informational message.
::
::alert{type="warning" icon="heroicons:exclamation-triangle"}
**Warning:** Please read carefully before proceeding.
::
::alert{type="error"}
An error occurred. Please try again.
::
::alert{type="success"}
Operation completed successfully!
::
Card with Slots
::card{image="/images/feature.jpg"}
#header
Feature Title
#default
This card showcases a new feature with an image header and action buttons.
#footer
:button[Learn More]{to="/features" variant="link"}
::
Accordion/Collapsible
::accordion
::accordion-item{title="What is Nuxt Content?"}
Nuxt Content is a file-based CMS for Nuxt applications.
::
::accordion-item{title="How do I install it?"}
Run `npx nuxi module add content` to add it to your project.
::
::
Tabs
::tabs
::tab{label="Vue"}
```vue
<template>
<div>Hello Vue!</div>
</template>
::
::tab{label="React"}
function Hello() {
return <div>Hello React!</div>
}
:: ::
### Badge/Tag
```markdown
Status: :badge[Published]{color="success"} :badge[Featured]{color="primary"}
Tags: :tag[Vue] :tag[Nuxt] :tag[TypeScript]
Icon
:icon{name="heroicons:home" class="w-5 h-5"}
:icon{name="lucide:github" size="24"}
:icon{name="mdi:vuejs" class="text-green-500"}
Callout with Icon
::callout{icon="heroicons:light-bulb"}
#title
Pro Tip
#default
Use MDC syntax to create rich, interactive documentation with Vue components.
::
Creating Custom Components
Create Vue components in components/content/ to use in MDC:
<!-- components/content/Alert.vue -->
<script setup lang="ts">
defineProps<{
type?: 'info' | 'warning' | 'error' | 'success'
icon?: string
}>()
</script>
<template>
<div :class="['alert', `alert-${type}`]">
<Icon v-if="icon" :name="icon" />
<slot />
</div>
</template>
Then use in Markdown:
::alert{type="info" icon="heroicons:information-circle"}
Custom alert component content.
::
Routing Best Practices
Avoiding Route Ambiguity with Optional Catch-All Routes
When creating a documentation section or any area using dynamic routes for content, avoid using optional catch-all routes like [[...slug]].vue if you also need to support the root path (e.g., /docs) and sub-paths (e.g., /docs/getting-started) reliably.
Nuxt/Vue Router can have trouble disambiguating the root path when using [[...slug]].vue.
Recommended Pattern: Split the implementation into two explicit files:
- •
pages/docs/index.vue- Handles the root/docspathvue<script setup> const { data: page } = await useAsyncData('docs-index', () => queryCollection('docs').path('/docs').first() ) // ... handle page not found ... </script> - •
pages/docs/[...slug].vue- Handles all nested paths (slug is required)vue<script setup> const route = useRoute() const slug = route.params.slug.join('/') const { data: page } = await useAsyncData(`docs-${slug}`, () => queryCollection('docs').path(`/docs/${slug}`).first() ) // ... handle page not found ... </script>
This ensures predictable routing behavior and prevents 404 errors on the root path.
References
- •ContentRenderer - Render parsed content in templates
- •Custom Components - Create MDC-compatible Vue components
- •Collections - Organize and query content
- •Prose Components - Customize default HTML rendering
- •MDC Module - Underlying MDC parser and syntax
- •Nuxt Content Docs - Official documentation