Table of Contents
- What is Nuxt Content?
- Converting old content to a new format (JSON to Markdown)
- Markdown features in Nuxt Content
- YAML Front Matter
- Using Vue Components
- Automatic Table of Contents
- Anchor Name Issue
- Fetching content and sorting
- Creating the table of contents from the toc array
- Conclusion
Nuxt recently updated their Content Module alongside the full static build and I've started using this combo in my projects. In particular, my personal website (the one your reading this on) had been using a git-based headless CMS, Netlify CMS which works and integrates well with Netlify hosting. Netlify CMS is great and provides a intuitive user interface that makes it easy to create and manage content but I prefer to use a first-party module as opposed to a third-party one most of the time and especially with an excellent framework like Nuxt. Netlify CMS does have the advantage of working with several frameworks, is open-source, and extensible.
What is Nuxt Content?
Nuxt Content is a git-based, headless CMS. Git-based makes it easy to integrate content creation and management into your workflow. Using a static build, we are able to render the content files and serve them as static assets on a CDN. This results is an optimized and performant site that serves a small payload and is quick to render.
You can install the Nuxt Content module by selecting the option during the installation process using
npx create-nuxt-app [name]
or manually after installation withnpm install @nuxtjs/content
.
Nuxt Content can be used to write content in the /content
directory, fetch it using the global $content
instance and display it inside a component with the <nuxt-content>
tag.
Converting old content to a new format (JSON to Markdown)
Before conversion my content was in JSON format from using the Netlify CMS editor. I could have left the content in JSON format since Nuxt Content supports a variety of formats but I wanted to use features that are available in Markdown only.
Nuxt Content supports Markdown, CSV, XML, YML/YAML, and JSON/JSON5.
Markdown features in Nuxt Content
YAML Front Matter
In Markdown files, you can use YAML front matter to easily add meta information to content. This is helpful for SEO concerns as well as including a thumbnail or author information for the content.
Using Vue Components
Vue components can be registered in the components/global
directory to be used in Markdown files and rendered in <nuxt-content>
. This feature is great for being able to reuse components for common pieces of content.
Automatic Table of Contents
A table of contents is automatically generated by using the h2 and h3 tags in the Markdown file and compiling them into an array of objects including the header id. We can iterate through those links in the template section of the components to display a clickable link for each section of the document.
Anchor Name Issue
One issue I ran into was with the anchors I was using for some elements. Heading tags (h1 - h6) are automatically adding to the toc
(table of contents) array. However, other element tags that you may want to link to would need an associated anchor tag with an id.
---
title: Anchor links for other elements
description: "How do I use anchor links with elements other than headings in Markdown
---
# Table of contents
<a href="#anchor">Anchor</a>
<a href="#non-header-anchor">Anchor</a>
## Anchor
<a id="non-header-anchor">Non header anchor</a>
Nuxt Content currently has a bug that prevents the Markdown parser from parsing anchor tags that are missing a href
attribute. A pull request has been accepted to fix it in a future release.
A workaround for this issue is to add an href
attribute with an empty value. This will prevent the error from occurring and file will be parse as normal.
hello.md
---
title: Anchor links for other elements
description: "How do I use anchor links with elements other than headings in Markdown
---
# Table of contents
<a href="#anchor">Displaying Content</a>
<a href="#non-header-anchor">Anchor</a>
## Anchor
<a href="" id="non-header-anchor">Non header anchor</a>
Even though the value is an empty string, it will still pass the check in the parser.
Fetching content and sorting
Nuxt Content makes it easy to fetch your content with both filter and sort methods. In their example Content blog, they use filter to pick out only the title and slug for the previous and next links then sort the results by the createdAt
attribute value which is automatically inserted into the YAML front matter when a new Markdown file is created in the /content
directory.
On my blog index page, I used a similar mindset to sort my posts in ascending date order which put the most recent posts at the top of the list.
pages/blog/index.vue
async asyncData({ $content, params }) {
const posts = await $content('blog', params.slug)
.sortBy('date', 'desc')
.fetch()
return {
posts
}
}
This could be refactored to use the filter method. While I'm using most of the content properties, I could use the
without(keys)
method to exclude the few I'm not using.
On the individual page for the blog post, we use the params
object once again to fetch the appropriate post.
async asyncData({ $content, params }) {
const post = await $content('blog', params.blog).fetch()
return {
post
}
},
params.blog
is used because my pages is named_blog
. Use the dynamic page name minus the underscore to get the correct parameter.
This lets you display the content of the file inside the <nuxt-content>
tag using the :document
attribute to pass the data.
<nuxt-content :document="post" />
toc
array
Creating the table of contents from the The Content module automatically creates a toc
array for the content file that consists of all the h2 and h3 tags present. You can use these 2 tags as headings in your Markdown file and then use the toc
to display a handy table of contents on the page.
We want to display the table of contents above the <nuxt-content>
tag.
<template>
<h2>
Table of Contents
</h2>
<ul>
<li v-for="link of post.toc" :key="link.id">
<NuxtLink :to="`#${link.id}`">{{ link.text }}</NuxtLink>
</li>
</ul>
</template>
This will loop through the toc
array and display a text link for each heading.
Conclusion
That's it for now. I still need to deep-dive into the Content module to learn more about what it can do. It is actively being developed right now so I'm sure there are plenty of new features (and bug fixes) to come. It already looks really strong and I'm excited for what is to come!