You are reading Nuxt Content V1 documentation. Read the latest version
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.
Markdown
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: Introduction
description: 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
}
Excerpt
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
}
Headings
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 & 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 & 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
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://v2.nuxt.com">External link html</a>
[External Link markdown](https://v2.nuxt.com)
Footnotes
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.
Codeblocks
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
```js{1,3-5}[server.js] const http = require('http') const bodyParser = require('body-parser') http.createServer((req, res) => { bodyParser.parse(req, (error, body) => { res.end(body) }) }).listen(3000) ```
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.
HTML
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:
- 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">
- You cannot use self-closing tags, i.e., this won't work:
<my-component/>
But this will:
<my-component></my-component>
Example
Say we have a Vue component called ExampleMultiselect.vue:
Please choose a *framework*:
<example-multiselect :options="['Vue', 'React', 'Angular', 'Svelte']"></example-multiselect>
Result
You can also define the options for components in your front matter:
---
multiselectOptions:
- VuePress
- Gridsome
- Nuxt
---
<example-multiselect :options="multiselectOptions"></example-multiselect>
These components will be rendered using the <nuxt-content>
component, see displaying content.
Templates
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.vue
content/
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
Example
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.
JSON / JSON5
Data defined will be injected into the document.
No body will be generated.
Arrays
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.
Example
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"
}
]
CSV
Rows will be assigned to body variable.
Example
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
XML will be parsed
Example
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"
]
}
]
}
}
YAML / YML
Data defined will be injected into the document.
No body will be generated.
Example
A file content/home.yaml
:
title: Home
description: Welcome!
Will be transformed into:
{
"dir": "/",
"slug": "home",
"path": "/home",
"extension": ".yaml",
"title": "Home",
"description": "Welcome!"
}