How to build a static blog part 9
Sunday, 9 July 2023The blog and content
We're using Nuxt Content here, a really nice package that enables us to create and render static blog pages written in markdown, with not much configuration.
First, create a folder in your pages
directory called blog
. In there make a new file called index.vue
. This will be the page which displays a list of our blog posts.
<script setup lang="ts">
import { Article } from '~/types/types'
useHead({
title: 'Recent Blog Posts',
meta: [
{ name: 'description', content: 'Index of recent blog posts by me' },
],
})
// this returns content typed as 'ParsedContent'
const parsedContent = await queryContent('blog')
.sort({ date: -1 })
.only(['_path', 'title', 'description', 'tags'])
.find()
// This converts it to an Article[] type for our component
const articles = computed(() => {
return parsedContent as Article[]
})
</script>
<template>
<section>
<h1>Recent Posts</h1>
<hr />
<BlogPostsList
v-if="articles.length > 0"
:articles="articles"
></BlogPostsList>
<p v-else>Sorry, nothing found.</p>
</section>
</template>
Notice the way we’re getting the posts using a query. This is a feature of the Nuxt Content package. More in the docs. Notice as well I've added a sprinkle of Typescript here. I think it is worth using Typescript when we are dealing with data that we are loading from elsewhere. Ignore any Typescript errors in this component, we'll get to them in a mo.
Then we need the component we're referencing above, so in the components
directory, create BlogPostsList.vue
.
<script lang="ts" setup>
import { Article } from '~/types/types'
defineProps({
articles: {
type: Array<Article>,
default: () => {
return []
},
},
})
</script>
<template>
<nav>
<ul class="list-none pl-0">
<li
v-for="{ title, _path, description, tags } in articles"
:key="_path"
>
<NuxtLink :to="_path" class="transition">
{{ title }}
</NuxtLink>
<br />
<small>{{ description }}</small>
<br />
<BlogTag v-for="tag in tags" :key="tag" :text="tag" />
<br />
</li>
</ul>
</nav>
</template>
Lets create the types file to keep our friend Typescript happy... Here ya go, create a types/types.ts
file:
export interface Article {
title: string
_path: string
description: string
tags?: string[]
}
There is also a little component to render the tags for our blog posts, so in the components
directory, create a sub -directory called content
. Components placed in this folder can be used in our markdown files. Create a new file called BlogTag.vue
<template>
<NuxtLink
:to="`/blog/tag/${text}`"
class="bg-slate-300 dark:bg-slate-700 rounded-full px-4 py-1 no-underline mr-2 text-sm transition"
>{{ text }}</NuxtLink
>
</template>
<script setup>
defineProps({
text: { type: String, default: '' },
})
</script>
Now we need the page which actually shows a blog post. In pages
make a file and call it [...slug].vue
This tells Nuxt that we want our Blog posts to have dynamic slugs and be available at a URL like this: https://my-super-blog/blog/the-blog-slug
.
<script setup>
const options = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
}
</script>
<template>
<div>
<article>
<ContentDoc>
<template #not-found >
<h1>Document not found</h1>
</template>
<template #default="{ doc }">
<BlogTag
v-for="tag in doc.tags"
:key="tag"
:text="tag"
></BlogTag>
<p>
<h1>{{ doc.title }}</h1>
<small>{{new Date(doc.date).toLocaleDateString('en-GB', options)}}</small>
</p>
<hr />
<ContentRenderer :value="doc" />
</template>
</ContentDoc>
</article>
<hr />
<BackButton to="/blog"></BackButton>
</div>
</template>
Let’s add a link to our blog page in the Navbar. In SiteHeader.vue
, add another entry in the links
array:
{ name: 'blog', to: '/blog', },
Now all that's left to do is add some content and our blog is ready. Create a file called test-post.md
in a folder called blog
, inside the content
folder, and add some front matter (the meta data about the post), then some text to render.
---
title: "Test post"
description: "this is the description of the test"
date: "2023-01-08" #YYYY-MM-DD
draft: false
tags: ["nuxt3", "test"]
---
Lorem ipsum etc...Put your post here
All being well you should be able to see the test post show up on the blog index page, and be able to click the link to read the post. Future posts can be added the same way, and they will show up in the index page. This blog does not feature any pagination, or search functionality. I suppose that wouldn't take much effort to add, but I don't intend to write enough posts to warrant all that!