# Markdown

> Create and query Markdown files in your Nuxt applications and use the MDC syntax to integrate Vue components.

## Usage

### Define a Collection

```ts [content.config.ts]
import { defineCollection, defineContentConfig } from '@nuxt/content'
import { z } from 'zod'

export default defineContentConfig({
  collections: {
    blog: defineCollection({
      type: 'page',
      source: 'blog/*.md',
      schema: z.object({
        date: z.string()
      })
    })
  }
})
```

<note to="/docs/collections/types#page-type">

Learn more about the `page` collection type.

</note>

### Create `.md` files

Create blog posts in `content/blog/` directory.

<code-group>

```md [foo.md]
---
date: 2020-11-11
---

# Foo

This is Foo blog post.
```

```md [bar.md]
---
date: 2024-12-12
---
Hello
I am bar. Nice to meet you.
```

</code-group>

### Query Markdown Files

Now we can query blog posts:

```ts
// Get the foo post
const fooPost = await queryCollection('blog').path('/blog/foo').first()

// Find all posts
const allPosts = await queryCollection('blog').order('date', 'DESC').all()
```

### Display Markdown

To display the content of a markdown file, you can use the [`<ContentRenderer>`](/docs/components/content-renderer) component.

```vue [blog/[slug].vue]
<script setup>
const slug = useRoute().params.slug
const { data: post } = await useAsyncData(`blog-${slug}`, () => {
  return queryCollection('blog').path(`/blog/${slug}`).first()
})
</script>

<template>
  <!-- Render the blog post as Prose & Vue components -->
  <ContentRenderer :value="post" />
</template>
```

<note>

Read more about the [`<ContentRenderer>`](/docs/components/content-renderer) component and [`Prose Components`](/docs/components/prose).

</note>

## Frontmatter

Frontmatter is a convention of Markdown-based CMS to provide meta-data to pages, like description or title. In Nuxt Content, the frontmatter uses the YAML syntax with `key: value` pairs.

These data are available when rendering the content and can store any information that you would need.

### Syntax

You can declare a frontmatter block at the top of the Markdown files in the `content/` directory with the `---` identifier.

```md [content/index.md]
---
title: 'Title of the page'
description: 'meta description of the page'
---

<!-- Content of the page -->
```

```ts [example.ts]
const home = await queryCollection('content').path('/').first()

console.log(home.title)
// => 'Title of the page'
console.log(home.description)
// => 'meta description of the page'
console.log(home.body)
// => AST object of the page content
```

### Native parameters

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      
    </th>
    
    <th>
      
    </th>
    
    <th>
      
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Key
    </td>
    
    <td>
      Type
    </td>
    
    <td>
      Default
    </td>
    
    <td>
      Description
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        title
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      First <code>
        <h1>
      </code>
      
       of the page
    </td>
    
    <td>
      Title of the page, will also be injected in metas
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        description
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      First <code>
        <p>
      </code>
      
       of the page
    </td>
    
    <td>
      Description of the page, will be shown below the title and injected into the metas
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        navigation
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Define if the page is included in <a href="/docs/utils/query-collection-navigation">
        <code>
          queryCollectionNavigation
        </code>
      </a>
      
       return value.
    </td>
  </tr>
</tbody>
</table>

<warning>

Additional parameters that you have defined in your frontmatter block need to be defined in your schema (see the date parameter in the example at top of this page) to be able to use them for querying.

</warning>

## MDC Syntax

We created the MDC syntax to supercharge Markdown and give you the ability to integrate Vue components with slots and props inside your Markdown.

<note to="https://remark-mdc.nuxt.space/#syntax">

Explore the full MDC documentation.

</note>

<callout icon="i-simple-icons-visualstudiocode" to="https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc">

Install the **MDC VS Code extension** to get proper syntax highlighting for the MDC syntax.

</callout>

### Vue Components

You can use any Vue component in your Markdown files.

We have a special syntax to make it easier to use components in your Markdown files.

```mdc [content/index.md]
::component-name
Default slot content
::
```

<warning>

Components that are used in Markdown have to be marked as `global` in your Nuxt app if you don't use the `components/content/` directory. Visit [Nuxt 3 docs](https://nuxt.com/docs/guide/directory-structure/components) to learn more.

</warning>

#### Block Components

Block components are components that accept Markdown content or another component as a slot.

The component must contain at least one `<slot />` component to accept formatted text.

In a markdown file, use the component with the `::` identifier.

<code-group>

```mdc [index.md]
::card
The content of the card
::
```

```html [Card.vue]
<!-- components/content/Card.vue -->
<template>
  <div class="p-2 border bg-white dark:bg-black dark:border-gray-700 rounded">
    <slot />
  </div>
</template>
```

<code-preview label="Preview" icon="i-lucide-eye">
<example-card>

The content of the card

</example-card>
</code-preview>
</code-group>

#### Slots

A component's slots can accept content or another components.

- **Default slot** renders the top-level content inside the block component or with `#default`
- **Named slots** use the `#` identifier to render the corresponding content.

<code-group>

```mdc [index.md]
::hero
My Page Title

#description
This will be rendered inside the `description` slot.
::
```

```html [Hero.vue]
<template>
  <section>
    <h1 class="text-4xl">
      <slot mdc-unwrap="p" />
    </h1>
    <slot name="description" />
  </section>
</template>
```

<code-preview label="Preview" icon="i-lucide-eye">
<example-hero>

My Page Title<template v-slot:description="">

This will be rendered inside the `description` slot.

</template>
</example-hero>
</code-preview>
</code-group>

<note>

Read more about the [`<slot />`](/docs/components/slot) component.

</note>

<tip>

You can use Markdown inside your components slots:

<code-group>

```mdc [index.md]
::my-title
A [rich text](/) will be **rendered** by the component.
::
```

```html [MyTitle.vue]
<template>
  <h1 class="text-4xl">
    <slot mdc-unwrap="p" />
  </h1>
</template>
```

<code-preview label="Preview" icon="i-lucide-eye">
<example-title>

A [rich text](/) will be **rendered** by the component.

</example-title>
</code-preview>
</code-group>
</tip>

#### Props

There are two ways to pass props to components using MDC.

##### **Inline method**

The `{}` identifier passes props to components in a terse way by using a `key=value` syntax.

<code-group>

```mdc [index.md]
::alert{type="warning"}
The **alert** component.
::
```

```vue [Alert.vue]
<script setup>
const props = defineProps({ type: { type: String } })

const alertClass = computed(() => {
  return {
    warning: 'bg-orange-100 border-orange-200 dark:bg-orange-900 dark:border-orange-800',
    info: 'bg-blue-100 border-blue-200 dark:bg-blue-900 dark:border-blue-800',
    success: 'bg-green-100 border-green-200 dark:bg-green-900 dark:border-green-800',
  }[props.type]
})
</script>

<template>
  <div
    class="text-black p-2 border dark:text-white rounded"
    :class="alertClass"
  >
    <slot mdc-unwrap="p" />
  </div>
</template>
```

<code-preview label="Preview" icon="i-lucide-eye">
<example-alert type="warning">

The **alert** component.

</example-alert>
</code-preview>
</code-group>

Multiple props can be separated with a space:

```mdc
::alert{type="warning" icon="exclamation-circle"}
Oops! An error occurred
::
```

The `v-bind` shorthand `:` can be also be used to bind a prop to a value in the frontmatter.

```mdc
---
type: "warning"
---

::alert{:type="type"}
Your warning
::
```

If you want to pass arrays or objects as props to components you can pass them as JSON string and prefix the prop key with a colon to automatically decode the JSON string. Note that in this case you should use single quotes for the value string so you can use double quotes to pass a valid JSON string:

<code-group>

```mdc [array.md]
::dropdown{:items='["Nuxt", "Vue", "React"]'}
::
```

```mdc [number-array.md]
::dropdown{:items='[1,2,3.5]'}
::
```

```mdc [object.md]
::chart{:options='{"responsive": true, "scales": {"y": {"beginAtZero": true}}}'}
::
```

</code-group>

##### **YAML method**

The YAML method uses the `---` identifier to declare one prop per line, that can be useful for readability.

<code-group>

```mdc [index.md]
::icon-card
---
icon: IconNuxt
description: Harness the full power of Nuxt and the Nuxt ecosystem.
title: Nuxt Architecture.
---
::
```

```html [IconCard.vue]
<script setup>
defineProps({
  title: {
    type: String,
    default: 'Default title'
  },
  description: {
    type: String,
    default: 'Default description'
  },
  icon: {
    type: String,
    default: 'IconMarkdown'
  }
})
</script>

<template>
  <div class="p-6 border bg-white dark:bg-black dark:border-gray-700 rounded">
    <component :is="icon" class="w-20 h-20" />
    <h2 class="text-3xl font-semibold mb-2">
      {{ title }}
    </h2>
    <p>{{ description }}</p>
  </div>
</template>
```

<code-preview label="Preview" icon="i-lucide-eye">
<example-icon-card description="Harness the full power of Nuxt and the Nuxt ecosystem." icon="i-simple-icons-nuxtdotjs" title="Nuxt Architecture.">



</example-icon-card>
</code-preview>
</code-group>

### Attributes

Attributes are useful for highlighting and modifying part of paragraph. The syntax is nearly similar to inline components and markdown links syntax.

Possible values are all named attributes, classes with the notation `.class-name` and an ID with `#id-name`.

<code-group>

```mdc [index.md]
Hello [World]{style="color: green;" .custom-class #custom-id}!
```

<code-preview label="Preview" icon="i-lucide-eye">

Hello <span className="custom-class" id="custom-id" style="color: green;">

World

</span>

 !

</code-preview>
</code-group>

In addition to mdc components and `span`, attribute syntax will work on images, links, inline `code`, **bold** and _italic_ text.

<code-group>

```md [index.md]
Attributes work on:

- [Attributes](#attributes){style="background-color: green;"}, `code`{style="color: cyan;"},
- _italic_{style="background-color: yellow; color:black;"} and **bold**{style="background-color: lightgreen;"} texts.
```

<code-preview label="Preview" :prose="true">

Attributes work on:

- [Attributes](#attributes), `code`,
- *italic* and **bold** texts.

</code-preview>
</code-group>

### Binding Data

You can bind data within your Markdown document using the `{{ $doc.variable || 'defaultValue' }}` syntax. These values can be defined in the YAML frontmatter at the top of the document, within each MDC component, or injected using the `data` prop of the `<ContentRenderer>` component.

#### Define in YAML

```mdc
---
title: 'Title of the page'
description: 'meta description of the page'
customVariable: 'Custom Value'
---

# The Title is {{ $doc.title }} and customVariable is {{ $doc.customVariable || 'defaultValue' }}
```

#### Define in external with `<ContentRenderer>`

```html [test.vue]
<template>
  <div>
    <ContentRenderer :value="data" :data="mdcVars"/>
    <button type="button" v-on:click="mdcVars.name = 'Hugo'">Change name</button>
  </div>
</template>

<script setup lang="ts">
const { data } = await useAsyncData(() => queryCollection('content').path('/test').first());
const mdcVars = ref({ name: 'Maxime'});
</script>
```

```mdc [test.md]
# Hello {{ $doc.name || 'World' }}
```

## Prose Components

In Nuxt Content, the prose represents HTML tags generated by the Markdown syntax, such as heading levels and links.

For each HTML tag, a Vue component is used, allowing you to override them if needed, for example `<p>` becomes `<ProseP>`.

If you want to customize a Prose component, here are the recommended steps:

- Check out the original [component sources](https://github.com/nuxt-modules/mdc/blob/main/src/runtime/components/prose).
- Use the exact same props.
- In your `components/content/` directory, give it the same name.
- Make it yours 🚀.

<note to="/docs/components/prose">

Read the complete Prose reference in the Prose Components section.

</note>

## Code Highlighting

Nuxt Content uses [Shiki](https://github.com/shikijs/shiki), which colors tokens with VSCode themes.

Code highlighting works both on [`ProsePre`](/docs/components/prose#prosepre) and [`ProseCode`](/docs/components/prose#prosecodeinline).

Each line of a code block gets its line number in the `line` attribute so lines can be labeled or individually styled.

<callout>

[Read the API reference to configure or entirely disable syntax highlighting.](/docs/getting-started/configuration)

</callout>

## Images

You can add images to your `public` directory:

```bash [Directory structure]
content/
  index.md
public/
  image.png
nuxt.config.ts
package.json
```

And then use them in your markdown files in the `content` directory as such:

```md [content/index.md]
![my image](/image.png)
```

## Excerpt

Content excerpt or summary can be extracted from the content using `<!--more-->` as a divider.

```md [content/index.md]
---
title: Introduction
---

Learn how to use `@nuxt/content`.

<!--more-->

Full amount of content beyond the more divider.
```

Description property will contain the excerpt content unless defined within the frontmatter props.

If there is no `<!--more-->` divider in the text then excerpt is undefined.

<tip>

You should define the `excerpt` field in the collection schema if you want to use the excerpt feature.

```ts [content.config.ts]
const content = defineCollection({
  type: 'page',
  source: '**',
  schema: z.object({
    excerpt: z.object({
      type: z.string(),
      children: z.any(),
    }),
  }),
})
```

Read more about the [collection schema](/docs/collections/define#collection-schema).

</tip>

Example variables will be injected into the document:

```json
{
  "excerpt": Object
  "body": Object
  // ... other keys
}
```
