Writing content

Learn how to write your content/, supporting Markdown, YAML, CSV and JSON.

First of all, create a content/ directory in your project:

content/  articles/    article-1.md    article-2.md  home.md

This module will parse .md, .yaml, .yml, .csv, .json, .json5, .xml files and generate the following properties:

  • dir
  • path
  • slug
  • extension (ex: .md)
  • createdAt
  • updatedAt

The createdAt and updatedAt properties are based on the file's actual created & updated datetime, but you can override them by defining your own createdAt and updatedAt values. This is especially useful if you are migrating your past blog posts where the createdAt can be months or years ago.


This module converts your .md files into a JSON AST tree structure, stored in a body variable.

Make sure to use the <nuxt-content> component to display the body of your markdown content, see displaying content.

You can check the basic syntax guide to help you master Markdown

Front Matter

You can add a YAML front matter block to your markdown files. The front matter must be the first thing in the file and must take the form of valid YAML set between triple-dashed lines. Here is a basic example:

---title: Introductiondescription: Learn how to use @nuxt/content.---

These variables will be injected into the document:

{  body: Object  excerpt: Object  title: "Introduction"  description: "Learn how to use @nuxt/content."  dir: "/"  extension: ".md"  path: "/index"  slug: "index"  toc: Array  createdAt: DateTime  updatedAt: DateTime}


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

---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 Front Matter props.

Be careful to enter <!--more--> exactly; i.e., all lowercase and with no whitespace.

Example variables will be injected into the document:

{  body: Object  title: "Introduction"  description: "Learn how to use @nuxt/content."  dir: "/"  excerpt: Object  extension: ".md"  path: "/index"  slug: "index"  toc: Array  createdAt: DateTime  updatedAt: DateTime}


This module automatically adds an id and a link to each heading.

Say we have the following markdown file:

# Lorem ipsum## dolor—sit—amet### consectetur &amp; adipisicing#### elit##### elit

It will be transformed to its JSON AST structure, and by using the nuxt-content component, it will render HTML like:

<h1 id="lorem-ipsum-"><a href="#lorem-ipsum-" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Lorem ipsum</h1><h2 id="dolorsitamet"><a href="#dolorsitamet" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>dolor—sit—amet</h2><h3 id="consectetur--adipisicing"><a href="#consectetur--adipisicing" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>consectetur &#x26; adipisicing</h3><h4 id="elit"><a href="#elit" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>elit</h4><h5 id="elit-1"><a href="#elit-1" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>elit</h5>

The links in headings are empty and therefore hidden, so it's up to you to style them. For an example, try hovering one of the headers in these docs.

Links are transformed to add valid target and rel attributes using remark-external-links. You can check here to learn how to configure this plugin.

Relative links are also automatically transformed to nuxt-link to provide navigation between page components with enhanced performance through smart prefetching.

Here is an example using external, relative, markdown and html links:

---title: Home---## Links<nuxt-link to="/articles">Nuxt Link to Blog</nuxt-link><a href="/articles">Html Link to Blog</a>[Markdown Link to Blog](/articles)<a href="https://nuxtjs.org">External link html</a>[External Link markdown](https://nuxtjs.org)


This module supports extended markdown syntax for footnotes using remark-footnotes. You can check here to learn how to configure this plugin.

Here is an example using footnotes:

Here's a simple footnote,[^1] and here's a longer one.[^bignote][^1]: This is the first footnote.[^bignote]: Here's one with multiple paragraphs and code.    Indent paragraphs to include them in the footnote.    `{ my code }`    Add as many paragraphs as you like.

You can check the extended syntax guide for more information about footnotes.


This module automatically wraps codeblocks and applies PrismJS classes (see syntax highlighting).

Codeblocks in Markdown are wrapped inside 3 backticks. Optionally, you can define the language of the codeblock to enable specific syntax highlighting.

Originally markdown did not support filenames or highlighting specific lines inside codeblocks. However, this module allows it with its own custom syntax:

  • Highlighted line numbers inside curly braces
  • Filename inside square brackets
const http = require('http')
const bodyParser = require('body-parser')

http.createServer((req, res) => {
  bodyParser.parse(req, (error, body) => {

After rendering with the nuxt-content component, it should look like this (without the filename yet):

<div class="nuxt-content-highlight">  <span class="filename">server.js</span>  <pre class="language-js" data-line="1,3-5">    <code>      ...    </code>  </pre></div>

Line numbers are added to the pre tag in data-line attribute.

Check out this comment on how to render prism line numbers.

Filename will be converted to a span with a filename class. It's up to you to style it.

Check out the main.css file of this documentation for an example on styling filenames.

Syntax highlighting

It supports by default code highlighting using PrismJS and injects the theme defined in options into your Nuxt.js app, see configuration.


You can write HTML in your Markdown:

---title: Home---## HTML<p><span class="note">A mix of <em>Markdown</em> and <em>HTML</em>.</span></p>

Beware that when placing Markdown inside a component, it must be preceded and followed by an empty line, otherwise the whole block is treated as custom HTML.

This won't work:

<div class="note">  *Markdown* and <em>HTML</em>.</div>

But this will:

<div class="note">  *Markdown* and <em>HTML</em>.</div>

As will this:

<span class="note">*Markdown* and <em>HTML</em>.</span>

Vue components

You can use global Vue components or locally registered in the page you're displaying your markdown.

An issue exists with locally registered components and live edit in development, since v1.5.0 you can disable it by setting liveEdit: false (see configuration).

Since @nuxt/content operates under the assumption that all Markdown is provided by the author (and not via third-party user submission), sources are processed in full (tags included), with a couple of caveats from rehype-raw:

  1. You need to refer to your components and their props by kebab case naming:
Use <my-component :my-prop="myValue"> instead of <MyComponent :myProp="myValue">
  1. You cannot use self-closing tags, i.e., this won't work:

But this will:



Say we have a Vue component called ExampleMultiselect.vue:

Please choose a *framework*:<example-multiselect :options="['Vue', 'React', 'Angular', 'Svelte']"></example-multiselect>


Please choose a framework:
Not working on content v2 docs!

You can also define the options for components in your front matter:

---multiselectOptions:  - VuePress  - Gridsome  - Nuxt---<example-multiselect :options="multiselectOptions"></example-multiselect>
Not working on content v2 docs!
These components will be rendered using the <nuxt-content> component, see displaying content.


You can use template tags for content distribution inside your Vue.js components:

<my-component>  <template #named-slot>    <p>Named slot content.</p>  </template></my-component>

However, you cannot render dynamic content nor use slot props. I.e., this wont work:

<my-component>  <template #named-slot="slotProps">    <p>{{ slotProps.someProperty }}</p>  </template></my-component>

Global components

Since v1.4.0 and Nuxt v2.13.0, you can now put your components in components/global/ directory so you don't have to import them in your pages.

components/  global/    Hello.vuecontent/  home.md

Then in content/home.md, you can use <hello></hello> component without having to worry about importing it in your page.

Table of contents

When fetching a document, we have access to a toc property which is an array of all the titles. Each title has an id so that it is possible to link to, a depth which is the type of heading it is. Only h2 and h3 titles are used for the toc. There is also a text property which is the text of the title.

{  "toc": [{    "id": "welcome",    "depth": 2,    "text": "Welcome!"  }]}

Take a look at the right side of this page for an example.

Check out this snippet on how to implement a table of contents into your app


A file content/home.md:

---title: Home---## Welcome!

Will be transformed into:

{  "dir": "/",  "slug": "home",  "path": "/home",  "extension": ".md",  "title": "Home",  "toc": [    {      "id": "welcome",      "depth": 2,      "text": "Welcome!"    }  ],  "body": {    "type": "root",    "children": [      {        "type": "element",        "tag": "h2",        "props": {          "id": "welcome"        },        "children": [          {            "type": "element",            "tag": "a",            "props": {              "ariaHidden": "true",              "href": "#welcome",              "tabIndex": -1            },            "children": [              {                "type": "element",                "tag": "span",                "props": {                  "className": [                    "icon",                    "icon-link"                  ]                },                "children": []              }            ]          },          {            "type": "text",            "value": "Welcome!"          }        ]      }    ]  }}

We internally add a text key with the markdown body that will be used for searching or extending it.


Data defined will be injected into the document.

No body will be generated.



You can now use arrays inside your .json files. Objects will be flattened and inserted into the collection. You can fetch your content in the same way as your used to.

Since the slug is by default taken from the path and missing in this case, you have to define it in your objects for this feature to work properly.

Check out our example with articles and authors.


A file content/home.json:

{  "title": "Home",  "description": "Welcome!"}

Will be transformed into:

{  "dir": "/",  "slug": "home",  "path": "/home",  "extension": ".json",  "title": "Home",  "description": "Welcome!"}

A file content/authors.json:

[  {    "name": "Sébastien Chopin",    "slug": "atinux"  },  {    "name": "Krutie Patel",    "slug": "krutiepatel"  },  {    "name": "Sergey Bedritsky",    "slug": "sergeybedritsky"  }]

Will be transformed into:

[  {    "name": "Sébastien Chopin",    "slug": "atinux",    "dir": "/authors",    "path": "/authors/atinux",    "extension": ".json"  },  {    "name": "Krutie Patel",    "slug": "krutiepatel",    "dir": "/authors",    "path": "/authors/krutiepatel",    "extension": ".json"  },  {    "name": "Sergey Bedritsky",    "slug": "sergeybedritsky",    "dir": "/authors",    "path": "/authors/sergeybedritsky",    "extension": ".json"  }]


Rows will be assigned to body variable.


A file content/home.csv:

title, description
Home, Welcome!

Will be transformed into:

{  "dir": "/",  "slug": "home",  "path": "/home",  "extension": ".csv",  "body": [    {      "title": "Home",      "description": "Welcome!"    }  ]}


XML will be parsed


A file content/home.xml:

<xml>  <item prop="abc">    <title>Title</title>    <description>Hello World</description>  </item></xml>

Will be transformed into:

{  "dir": "/",  "slug": "home",  "path": "/home",  "extension": ".xml",  "body": {    "xml": {      "item": [        {          "$": {            "prop": "abc"          },          "title": [            "Title"          ],          "description": [            "Hello World"          ]      }    ]  }}


Data defined will be injected into the document.

No body will be generated.


A file content/home.yaml:

title: Homedescription: Welcome!

Will be transformed into:

{  "dir": "/",  "slug": "home",  "path": "/home",  "extension": ".yaml",  "title": "Home",  "description": "Welcome!"}