Snippets
Check out these code snippets that can be copied directly into your application.
Usage
asyncData
export default {
async asyncData({ $content, params }) {
const article = await $content('articles', params.slug).fetch()
return {
article
}
}
}
head
Add dynamic metas based on title and description defined in the front-matter:
export default {
async asyncData({ $content, params }) {
const article = await $content('articles', params.slug).fetch()
return {
article
}
},
head() {
return {
title: this.article.title,
meta: [
{ hid: 'description', name: 'description', content: this.article.description },
// Open Graph
{ hid: 'og:title', property: 'og:title', content: this.article.title },
{ hid: 'og:description', property: 'og:description', content: this.article.description },
// Twitter Card
{ hid: 'twitter:title', name: 'twitter:title', content: this.article.title },
{ hid: 'twitter:description', name: 'twitter:description', content: this.article.description }
]
}
}
}
Features
Search
Add a search input component by using watch:
<template>
<div>
<input v-model="query" type="search" autocomplete="off" />
<ul v-if="articles.length">
<li v-for="article of articles" :key="article.slug">
<NuxtLink :to="{ name: 'blog-slug', params: { slug: article.slug } }">{{ article.title }}</NuxtLink>
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
query: '',
articles: []
}
},
watch: {
async query (query) {
if (!query) {
this.articles = []
return
}
this.articles = await this.$content('articles')
.only(['title', 'slug'])
.sortBy('createdAt', 'asc')
.limit(12)
.search(query)
.fetch()
}
}
}
</script>
Check out the search documentation
Prev and Next
Add previous and next links using the surround
method:
<template>
<div>
<NuxtLink v-if="prev" :to="{ name: 'blog-slug', params: { slug: prev.slug } }">
{{ prev.title }}
</NuxtLink>
<NuxtLink v-if="next" :to="{ name: 'blog-slug', params: { slug: next.slug } }">
{{ next.title }}
</NuxtLink>
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const [prev, next] = await $content('articles')
.only(['title', 'slug'])
.sortBy('createdAt', 'asc')
.surround(params.slug)
.fetch()
return {
prev,
next
}
}
}
</script>
Check out the surround documentation
Table of contents
Add a table of contents by looping over our array of toc and use the id
to link to it and the text
to show the title. We can use the depth
to style the titles differently:
<template>
<ul>
<li
v-for="link of article.toc"
:key="link.id"
:class="{ 'toc2': link.depth === 2, 'toc3': link.depth === 3 }"
>
<NuxtLink :to="`#${link.id}`">{{ link.text }}</NuxtLink>
</li>
</ul>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const article = await $content('articles', params.slug)
.fetch()
return {
article
}
}
}
</script>
Check out the Table of contents documentation
Dynamic routing
Let's say you want to create an app with routes following the content/
file structure, you can do so by creating a pages/_.vue
component:
<script>
export default {
async asyncData ({ $content, app, params, error }) {
const path = `/${params.pathMatch || 'index'}`
const [article] = await $content({ deep: true }).where({ path }).fetch()
if (!article) {
return error({ statusCode: 404, message: 'Article not found' })
}
return {
article
}
}
}
</script>
This way, if you go the /themes/docs
route, it will display the content/themes/docs.md
file. If you need an index page for your directories, you need to create a file with the same name as the directory:
content/
themes/
docs.md
themes.md
Don't forget to prefix your calls with the current locale if you're using nuxt-i18n
.
Custom Highlighter
Highlight.js
import highlightjs from 'highlight.js'
const wrap = (code, lang) => `<pre><code class="hljs ${lang}">${code}</code></pre>`
export default {
// Complete themes: https://github.com/highlightjs/highlight.js/tree/master/src/styles
css: ['highlight.js/styles/nord.css'],
modules: ['@nuxt/content'],
content: {
markdown: {
highlighter(rawCode, lang) {
if (!lang) {
return wrap(highlightjs.highlightAuto(rawCode).value, lang)
}
return wrap(highlightjs.highlight(lang, rawCode).value, lang)
}
}
}
}
Shiki
Shiki is syntax highlighter that uses TexMate grammar and colors the tokens with VS Code themes. It will generate HTML that looks like exactly your code in VS Code.
You don't need to add custom styling, because Shiki will inlined it in the HTML.
import shiki from 'shiki'
export default {
modules: ['@nuxt/content'],
content: {
markdown: {
async highlighter() {
const highlighter = await shiki.getHighlighter({
// Complete themes: https://github.com/shikijs/shiki/tree/master/packages/themes
theme: 'nord'
})
return (rawCode, lang) => {
return highlighter.codeToHtml(rawCode, lang)
}
}
}
}
}
Shiki Twoslash
Twoslash is a markup format for TypeScript code. Internally, Twoslash uses TypeScript compiler to generate rich highlight info.
To get better idea how Twoslash works, you can get over to the Official TypeScript Documentation and hover some code example there.
You can achieve the same result by using Shiki Twoslash. This package is also the one that power Official TypeScript Documentation.
import {
createShikiHighlighter,
runTwoSlash,
renderCodeToHTML
} from 'shiki-twoslash'
export default {
modules: ['@nuxt/content'],
content: {
markdown: {
async highlighter() {
const highlighter = await createShikiHighlighter({
// Complete themes: https://github.com/shikijs/shiki/tree/master/packages/themes
theme: 'nord'
})
return (rawCode, lang) => {
const twoslashResults = runTwoSlash(rawCode, lang)
return renderCodeToHTML(
twoslashResults.code,
lang,
['twoslash'],
{},
highlighter,
twoslashResults
)
}
}
}
}
}