Nuxt Markdown has been deprecated in favour of @nuxt/content.
Nuxt Markdown is a simple but highly-configurable Nuxt.js module which allows you to use Markdown files for the content of your website. This makes it the perfect module to port your site over from Jekyll, or another static site generator.
Contents
- No configuration required
- Highly-configurable when you want it to be
- Supports static site generation
- Nested heirarchy of Markdown content
- Use Vue components in your Markdown
- Access and transform the front matter in your Markdown
- Uses markdown-it and gray-matter which you can configure
- Supports excerpts of Markdown files
- Easy to create taxonomy pages
- Add
nuxt-markdown
as adevDependency
to your project
yarn add nuxt-markdown --dev
# Or
npm install nuxt-markdown --save-dev
- Add
nuxt-markdown
to thebuildModules
section ofnuxt.config.js
export default {
buildModules: ['nuxt-markdown']
}
- Make a
content
directory and add some Markdown files to it, feel free to add some nested directories as well. You'll also need to create a corresponding Nuxt.js page in which you can access your Markdown content.
├── content
| ├── blog
| | ├── another-blog-post.md
| | └── my-first-blog-post.md
| └── index.md
├── pages
| ├── blog
| | └── _.vue
| └── index.vue
└── nuxt.config.js
-
Nuxt Markdown injects
$markdown
into the context and Vue instances of your app. By default,$markdown
exposes:$markdown.content
: an array of the front matter from all of the Markdown files inside yourcontent
directory.$markdown.loadContent()
: function to load the content of the Markdown file corresponding to the current route. Returns a Vue component, which can be used as a dynamiccomponent
.$markdown.loadData()
: function to load the front matter of the Markdown file corresponding to the current route.
<template>
<main>
<h1>{{ blogPostFrontMatter.title }}</h1>
<component :is="blogPostContent" />
<ul>
<li v-for="blogPost in allBlogPosts" :key="blogPost.path">
<nuxt-link :to="blogPost.path">
{{ blogPost.title }}
</nuxt-link>
</li>
</ul>
</main>
</template>
<script>
export default {
asyncData({ app }) {
return {
allBlogPosts: app.$markdown.content,
blogPostFrontMatter: app.$markdown.loadData()
}
},
data() {
return {
blogPostContent: null
}
},
created() {
this.blogPostContent = () => this.$markdown.loadContent()
}
}
</script>
You can configure Nuxt Markdown in your nuxt.config.js
by either passing options directly or using top level options:
export default {
buildModules: [
['nuxt-markdown', {
collections: [],
grayMatter: {}
}]
],
markdown: {
collections: [],
grayMatter: {}
}
}
Nuxt Markdown allows you to have multiple collections
of Markdown files. Each collection is described by a configuration object. Here are the configuration options for a collection:
Name | Description | Type |
---|---|---|
name |
Name of the collection | String |
directory |
Directory of the Markdown files | String |
includeSubdirectories |
If the collection should include nested directories | Boolean |
routePrefix |
Prefix of the collection routes | String |
serverTransform |
Function to transform the collection on the server | Function |
clientTransform |
Function to transform the collection on the client | Function |
The name of the collection. You'll use this to refer to the collection in any page of your site. For example, if you named a collection blog
, you could show a list of blog posts like so:
<template>
<ol>
<li v-for="blogPost in blogPosts" :key="blogPost.title">
{{ blogPost.title }}
</li>
</ol>
</template>
<script>
export default {
asyncData({ app }) {
return {
blogPosts: app.$markdown.blog // This is the important bit!
}
}
}
</script>
The directory in which your content lives. In this example, directory
would be content/blog
. It's good to be as specific as possible (instead of relying on includeSubdirectories
), as every Markdown file within directory
is included into its own client-side bundle.
├── content
| └── blog
| ├── another-blog-post.md
| └── my-first-blog-post.md
└── nuxt.config.js
If includeSubdirectories
is false
, Nuxt Markdown will only look in directory
for Markdown files and not inside subdirectories. If includeSubdirectories
is true
, Nuxt Markdown will look in subdirectories for Markdown files, and the subdirectory will form part of the route for that Markdown file. Given the following file structure and configuration:
├── content
| └── blog
| ├── nuxt
| | ├── another-post-about-nuxt.md
| | └── how-to-build-a-blog-with-nuxt.md
| ├── another-blog-post.md
| └── my-first-blog-post.md
└── nuxt.config.js
export default {
markdown: {
collections: [
{
name: 'blog',
directory: 'content/blog',
includeSubdirectories: true,
routePrefix: '/blog/'
}
]
}
}
The following routes will be generated:
/blog/another-blog-post
/blog/my-first-blog-post
/blog/nuxt/another-post-about-nuxt
/blog/nuxt/how-to-build-a-blog-with-nuxt
The prefix of the route which will be generated for each file in the collection. This doesn't necessarily need to match the directory
of the collection (but often will). Given the same example above, if the route prefix was changed to /posts/
export default {
markdown: {
collections: [
{
name: 'blog',
directory: 'content/blog',
includeSubdirectories: true,
routePrefix: '/posts/'
}
]
}
}
Then the following routes would be generated:
/posts/another-blog-post
/posts/my-first-blog-post
/posts/nuxt/another-post-about-nuxt
/posts/nuxt/how-to-build-a-blog-with-nuxt
You may want to add properties to the front matter of your content, or change the order of a collection. serverTransform
and clientTransform
can be used to do this, on the server- and client-side respectively.
Nuxt Markdown uses gray-matter to read the front matter and content of Markdown files on the server-side. serverTransform
is a function, which takes the following parameters:
-
collection
, an array offile
objects from gray-matter. For more information about thefile
object, see the documentation here. -
helpers
, an object containing helper functions. Currently, this only includes:generateRoutes
, a function to generate routes. This can be used in conjunction withgenerate.routes
in yournuxt.config.js
.
// Generate a single route
generateRoutes('/categories')
// Generate multiple routes
generateRoutes(...['/categories/nuxt', '/categories/markdown'])
serverTransform
should return the collection. See the example for a better idea of how you can use serverTransform
.
Nuxt Markdown needs to serialize your front matter before injecting it into a Nuxt.js plugin, where it is deserialized; this means that everything is essentially passed through JSON.parse(JSON.stringify(collection))
. If your front matter contains any types unsupported by JSON, especially if your front matter contains dates, then clientTransform
can be used to reinstate these types. Try to use serverTransform
instead of clientTransform
where you can, as any code in clientTransform
will be included in the client-side bundle.
clientTransform
takes no arguments. However, it must return a function which takes the parameter:
collection
, an array of the front matter from your files (thedata
property from gray-matter).
The function returned by clientTransform
should return the collection. Check out the example which should make it clear!
By default, Nuxt Markdown puts class="nuxt-markdown"
on the root element of the content component (returned from loadContent()
). You can change this class name using the contentClassName
option.
export default {
markdown: {
contentClassName: 'my-amazing-markdown'
}
}
Will render:
<div class="my-amazing-markdown">
<!-- Markdown content here -->
</div>
Nuxt Markdown uses the default configuration of gray-matter. However, you can pass your own configuration to gray-matter, if for example, you want to take an excerpt from your Markdown content. Have a look at the gray-matter documentation for more information.
Note that Nuxt Markdown only retains the data
property from gray-matter, so you'll need to store the excerpt in data, using serverTransform
.
export default {
markdown: {
collections: [
{
name: 'blog',
includeSubdirectories: true,
serverTransform (collection) {
collection.forEach(({ data, excerpt }) => {
// Nuxt Markdown only retains the data from each collection
data.excerpt = excerpt
})
return collection
}
}
],
grayMatter: {
excerpt: true
}
}
}
By default, Nuxt Markdown parses Markdown with markdown-it. You can pass configuration to markdown-it using the markdownIt
option:
export default {
markdown: {
markdownIt: {
html: true,
xhtmlOut: true,
typographer: true
}
}
}
See the documentation for markdown-it configuration for more details.
Alternatively, you can pass an instance of markdown-it to the markdownIt
option. This is useful if you want to use markdown-it plugins.
export default {
markdown: {
markdownIt: require('markdown-it')()
.use(plugin1)
.use(plugin2, options)
.use(plugin3)
}
}
You can also use a custom Markdown parser if you wish, instead of markdown-it. The markdown
option expects a function which takes the Markdown source as its parameter and returns the compiled HTML.
export default {
markdown: {
markdown(source) {
return compileMarkdownIntoHTML(source)
}
}
}
This is an example configuration of Nuxt Markdown for a portfolio website, with blog posts and projects. First of all, the file structure:
├── content
| ├── posts
| | ├── nuxt
| | | ├── another-post-about-nuxt.md
| | | └── how-to-build-a-blog-with-nuxt.md
| | ├── another-blog-post.md
| | └── my-first-blog-post.md
| └── projects
| ├── nuxt-markdown.md
| └── my-website.md
├── pages
| ├── blog
| | └── _.vue
| ├── projects
| | └── _.vue
| └── index.vue
└── nuxt.config.js
The configuration of Nuxt Markdown in nuxt.config.js
:
export default {
buildModules: ['nuxt-markdown'],
markdown: {
collections: [
{
name: 'blog',
directory: 'content/posts',
includeSubdirectories: true,
routePrefix: '/blog/',
serverTransform(collection, { generateRoutes }) {
collection.forEach(({ content, data }) => {
data.mins = Math.round(content.split(' ').length / 250) || 1
data.tags = data.tags.map((tag) => {
return {
name: tag,
path: `/tags/${tag}`
}
})
generateRoutes(...data.tags.map((tag) => tag.path))
})
return collection.sort((a, b) => b.data.date - a.data.date)
},
clientTransform() {
return function(collection) {
collection.forEach((data) => {
data.date = new Date(data.date)
})
return collection
}
}
},
{
name: 'projects',
directory: 'content/projects',
includeSubdirectories: false,
routePrefix: '/projects/',
clientTransform() {
return function(collection) {
collection.forEach((data) => {
data.date = new Date(data.date)
})
return collection
}
}
}
]
}
}
The following routes will be generated:
/
/blog/another-blog-post
/blog/my-first-blog-post
/blog/nuxt/another-post-about-nuxt
/blog/nuxt/how-to-build-a-blog-with-nuxt
/projects/nuxt-markdown
/projects/my-website
# And routes for any tags in the Markdown front matter
/tags/nuxt
/tags/markdown
To show a list of blog posts on the home page (in index.vue
):
<template>
<ol>
<li v-for="blogPost in blogPosts" :key="blogPost.title">
<nuxt-link :to="blogPost.path">
<h1>{{ blogPost.title }}</h1>
</nuxt-link>
</li>
</ol>
</template>
<script>
export default {
asyncData({ app }) {
return {
blogPosts: app.$markdown.blog
}
}
}
</script>
And to show the content of an individual blog post, in blog/_.vue
:
<template>
<main>
<h1>{{ blogPost.title }}</h1>
<component :is="blogPostContent" />
</main>
</template>
<script>
export default {
asyncData({ app }) {
return {
blogPost: app.$markdown.loadData()
}
},
data() {
return {
blogPostContent: null
}
},
created() {
this.blogPostContent = () => this.$markdown.loadContent()
}
}
</script>
- Clone this repository
- Install dependencies using
yarn install
- Start development server using
yarn dev
Copyright © Greg Ives greg@gregives.co.uk (https://gregives.co.uk)