Nuxt is vue full stack framework. Content is a nuxt module to help you read content/
directory of your project and query the content from .md
, .yml
, .csv
and .json
files. Currently Nuxt3 is in candidate release 4 and soon it will be release to stable. Content v2 has already support for Nuxt3. There is already an official stater template called content-wind. We will be using this template to save some time and it is easy to start.
Features of template
- Create pages in Markdown in the
content/
directory - Use Nuxt layouts in your Markdown pages
- Enjoy meta tag generation from Markdown files
- Generated navigation based on your pages
- Create pages in Markdown in the
Switch between Light & Dark mode :moon:
Access 100,000 icons from 100+ icon sets with the
<Icon>
componentHighlight code blocks with Shiki
Create Vue components and use them in your Markdown
Deploy on any Node or Static hosting: GH Pages, Vercel, Netlify, Heroku, etc.
Installation
Run this command in your terminal to to install this template.
npx nuxi init my-website -t atinux/content-wind
Directory structure
/
|--- app/
| |-- router.option.ts
|--- components/
| |-- content/
| |-- Alert.vue
| |-- Icon.vue
| |-- List.vue
| |-- MarkdownBlock.vue
| |-- ColorModeSwitch.vue
| |-- Navbar.vue
|--- content/
| |-- 1.index.md
| |-- 2.about.md
|--- layouts/
| |-- default.vue
| |-- full-width.vue
|--- public/
| |-- cover.jpg
| |-- favicon.ico
|--- app.vue
|--- tailwind.config.js
- router.option.ts: This file will handle scroll position of previous route and smooth scroll to hash urls.
- components: Here you can define your vue components.
- components/content: Components from here will be use in the markdown files.
- content: Here you can write your pages content and it will be assign to route according to the file’s name.
- layouts: Define layouts of your website.
default.vue
will used everywhere if no custom layout is set. - public: Public folder of your website.
- app.vue: Main file of your project.
- tailwind.config.js: Tailwind css configuration file.
Writing your first article
Start with 1.index.md
file in the content/
directory. This file is your home page of your website /
. To define meta data of your pages, you can define it inside the ---
section of the markdown file. Since it markdown file you need to markdown syntax. You can also use vue
components inside markdown file. For inline component use :name-of-component
this syntax and for slot component start with ::name-of-component
and end with ::
. Below is example of MDC
syntax:
// meta data of page
---
title: 'My Page Title'
description: 'What a lovely page.'
image:
src: '/assets/image.jpg'
alt: 'An image showcasing My Page.'
width: 400
height: 300
head:
meta:
- name: 'keywords'
content: 'nuxt, vue, content'
- name: 'robots'
content: 'index, follow'
- name: 'author'
content: 'Fazail Alam'
---
// inline component with prop
// component from components/content/Icon.vue
:my-icon{name="ph:check-circle-light"}
// component with slots
::card{class="bg-white rounded-md"}
// title slot
#title
This is a good title.
// default slot
#default
Good content.
::
Creating blogs
Create a folder called blog
inside content/
directory. Inside this folder create 3(For this tutorial only, you can as many as you want) blogs and 1 index.md
.
content/
|---blog/
|--- 1.first blog.md
|--- 2.second blog.md
|--- 3.third blog.md
|--- index.md
---
navTitle: Blogs
layout: blogs
---
# Find All blog here
List Blog
---
head.title: My First Blog
image: cover.jpg
layout: blog
createdAt: 01-06-22
---
# Hello world
This is description.
---
head.title: Second Blog
image: cover.jpg
layout: blog
createdAt: 02-06-22
---
# Another blog
This is the second blog :sparkles:
---
head.title: Third Blog
image: cover.jpg
layout: blog
createdAt: 03-06-22
---
# Third blog
This is third blog :heart:
Customizing layout
Rename the full-width.vue
to blogs.vue
and create blog.vue
file. In blogs.vue file we will query the list of blog from content/blog/
directory and display it. Below the <slot />
tag add iteration of blog. In blog.vue file we display single post.
<template>
<div>
...
<slot />
<div v-for="post in posts" :key="post._path">
<img class="aspect-video object-cover object-center" :src="post.image" :alt="post.title" />
<NuxtLink :to="post._path">
<h2>{{ post.title }}</h2>
</NuxtLink>
<p>{{ post.description }}</p>
</div>
</div>
</template>
<script setup>
// using nuxt useAsyncData composable to query
const { data: posts } = await useAsyncData("posts", () => {
return queryContent("blog/")
.where({ _path: { $ne: "/blog" } }) // exclude content/blog/index.md
.sort({ createdAt: -1 }) // sort by createdAt desc
.only(['title','image','_path','description']) // only select these fields
.find();
});
</script>
<template>
<div
class="py-10 m-auto bg-white sm:px-8 sm:shadow dark:bg-gray-800 sm:rounded"
>
<main
class="max-w-2xl px-4 mx-auto prose sm:px-8 prose-gray dark:prose-invert"
>
// loading image from public directory
<img :src="`/${page.image}`" :alt="page.title" class="aspect-video" />
// Post content
<slot/>
<div class="flex justify-between items-center">
// Prevent showing content/blog/index.md route
// Previous Post Link
<NuxtLink v-if="prev && prev._path != '/blog'" :to="prev._path">
{{ prev.title }}
</NuxtLink>
<div v-else></div>
// Next Post Link
<NuxtLink v-if="next" :to="next._path">
{{ next.title }}
</NuxtLink>
<div v-else></div>
</div>
</main>
</div>
</template>
<script setup>
// get page data from useContent composable
const { next, prev, page } = useContent();
</script>
Our blog is ready. Now to run the project on development server run npm run dev -- -o
. -o
flag will automatically open the browser.