Create A Blog With NuxtJS

Create NuxtJS Application

The easiest way to create a NuxtJS application is by using the create-nuxt-app package. This can be installed by using the command.

yarn create nuxt-app <project-name>
cd <project-name>
yarn dev

Install Content Package

Now we have NuxtJS installed and running locally we need to install a package that will allow us to create content for the blog.

yarn add @nuxt/content
{ 
modules: [
'@nuxt/content'
],
content: { // Options }
}
--- 
title:
slug:
category:
extension: md
createdAt:
---

Default Layout

You can extend the main layout in NuxtJS by creating your own default layout, create a file in layouts/default.vue and it will be used for all your pages that you don't specify a layout.

<template>
<Nuxt />
</template>
<template>
<div>
<header-bar></header-bar>

<div class="w-full container-md mx-auto px-6">
<Nuxt />
</div>

<footer-bar></footer-bar>
</div>
</template>

Error Layout

The Error layout is the page that will be displayed when a 404/500 error code was returned. This should be placed inside the layouts folder layouts/error.vue but this is not a layout and should be used as a page, therefore do not include the <Nuxt /> tag inside the page.

<template>
<div class="my-8">
<h1>Page Not Found</h1>
</div>
</template>

<script>
export default {
layout: 'error',
props: {
error: {
type: String,
default: '404',
},
},
}
</script>

Homepage

The first page we’re going to create is the homepage of the blog, for this page we’re going to show a list of the 5 latest posts on the blog, create a new file in the pages directory of index.vue pages/index.vue

const articles = await $content('articles')
.sortBy('createdAt', 'desc')
.limit(5)
.fetch()
<template>
<div class="container">
<div class="border-b border-gray-500 pb-2 my-4" v-for="post in articles" :key="post.slug">
<h2 class="text-4xl mb-8 font-bold">
<NuxtLink :to="{ name: 'slug', params: { slug: post.slug.toLowerCase() } }">
{{ post.title }}
</NuxtLink>
</h2>
<nuxt-content :document="{ body: post.excerpt }" />
</div>
</div>
</template>

<script>
export default {
async asyncData({ $content, params }) {
const articles = await $content('articles')
.sortBy('createdAt', 'desc')
.limit(5)
.fetch()

return { articles }
},
}
</script>

Homepage SEO

Nuxt allows you to add global meta tags and SEO configs which is what we’ll use on the homepage, the alternative is to add meta information inside the page component itself.

export default {
// Global page headers (https://go.nuxtjs.dev/config-head)
head: {
title: 'Paulund',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{ rel: 'alternate', type: 'application/rss+xml', href: '/feed/rss.xml' },
],
},
}

Homepage Test

Add the NuxtJS test utils to your project.

yarn add --dev jest @nuxt/test-utils
module.exports = {
preset: '@nuxt/test-utils'
}
import { get, setupTest } from '@nuxt/test-utils'

describe('Homepage', () => {
setupTest({
server: true,
setupTimeout: 120000,
})

it('renders the index page', async () => {
const { body } = await get('/')

expect(body).toContain('<div class="container">')
})
})

Create A Single Post Page

Once you have the homepage the next page you can create is the single post page, this page will be linked from the homepage using the <NuxtLink> tag.

---
title:
slug:
category:
extension: md
createdAt:
---
<script>
export default {
async asyncData({ $content, params, error }) {
const post = await $content('articles', params.slug)
.fetch()
.catch((e) => {
error({ statusCode: 404, message: 'Page not found' })
})

const postDate = new Date(post?.createdAt)

return { post, postDate }
},

head() {
return {
title: this.post.title,
}
}
}
</script>
<template>
<div>
<div class="flex items-center pt-10 pb-4 mb-4">
<section class="w-full">
<h1 class="mt-4 mb-0 text-5xl">{{ post.title }}</h1>
</section>
</div>

<div class="mb-8">
<nuxt-content :document="post" />
</div>
</div>
</template>
<nuxt-content :document="post" />
---
title:
slug:
category:
extension: md
createdAt:
---
<script>
import titlefy from '~/mixins/titlefy'

export default {
mixins: [titlefy],

async asyncData({ $content, params }) {
const articles = await $content('articles')
.only(['title', 'slug', 'excerpt', 'category'])
.where({ category: params.slug })
.sortBy('createdAt', 'desc')
.fetch()

return { articles }
},

head() {
return {
title: this.titlefy(this.slug),
}
},
}
</script>
<template>
<div>
<div class="bg-gray-200 p-8 mb-8">
<h1 class="text-4xl">{{ titlefy(slug) }}</h1>
</div>

<div class="border-b border-gray-500 pb-2 my-4" v-for="post in articles" :key="post.slug">
<h2 class="text-4xl mb-8 font-bold">
<NuxtLink :to="{ name: 'slug', params: { slug: post.slug.toLowerCase() } }">
{{ post.title }}
</NuxtLink>
</h2>
<nuxt-content :document="{ body: post.excerpt }" />
</div>
</div>
</template>

Create a Search Component

Using the nuxt content package you have the feature of searching for the content inside your articles. We’re going to create a new component that gives us a search box for the user and then will show the blog post that match the search term.

<script>
export default {
data() {
return {
searchTerm: '',
searchResults: [],
}
},

methods: {
async searchPosts() {
if (this.searchTerm.length < 3) {
this.searchResults = []
return
}

// Search fields are defined in the configuration fullTextSearchFields, default ['title', 'description', 'slug', 'text']
this.searchResults = await this.$content('articles')
.search(this.searchTerm)
.limit(5)
.fetch()
},
},
}
</script>
<template>
<div
id="article-search"
class="flex items-center text-center py-12 mb-4 bg-gray-100"
>
<div class="w-full mx-auto px-6 flex-col">
<h1 class="mb-6">Search The Articles</h1>
<div class="mx-1">
<section>
<div>
<input
v-model="searchTerm"
type="search"
placeholder="Search for a article"
autocomplete="off"
@keydown="searchPosts"
/>
</div>
<div class="relative">
<section v-if="searchResults.length">
<NuxtLink
v-for="post in searchResults"
:key="post.slug"
:to="{
name: 'slug',
params: { slug: post.slug.toLowerCase() },
}"
class="w-full text-left bg-white block p-4 hover:bg-gray-100"
>
{{ post.title }}
</NuxtLink>
</section>
</div>
</section>
</div>
</div>
</div>
</template>

Create A Contact Page

When you have a blog post it’s important to have a contact page for your readers to be able to contact you. Nuxt doesn’t have this ability built-in by default by there are packages you can use to install to send a email from a contact form.

npm i nuxt-mail
modules: [
'@nuxt/content',
'@nuxtjs/axios',
['nuxt-mail', {
message: {
to: 'email@example.com',
},
smtp: {
host: "smtp.example.com",
port: 587,
auth: {
user: '',
pass: ''
},
},
}],
],
<template>
<div>
<h1>Contact</h1>

<div v-if="emailSent" class="alert alert-success">
<p>Email sent</p>
</div>

<form v-on:submit.prevent="submitForm" v-if="!emailSent">
<div>
<label>Name</label>
<p><input type="text" id="name" placeholder="Name" required="required" class="input" v-model="name"></p>
</div>

<div>
<label>Email</label>
<p><input type="text" id="email" placeholder="Email" required="required" class="input" v-model="email"></p>
</div>

<div>
<label>Message</label>
<p><textarea id="message" placeholder="Message" required="required" class="input" v-model="message"></textarea></p>
</div>

<div>
<p><button id="send" class="button is-primary">Send</button></p>
</div>
</form>
</div>
</template>

<script>
export default {
data() {
return {
name: '',
email: '',
message: '',
emailSent: false
}
},
methods: {
submitForm () {
this.$mail.send({
from: this.email,
subject: 'Contact form',
text: this.name + '<br>' + this.message,
}).then(() => {
this.emailSent = true
})
}
}
}
</script>

Add Google Analytics

There is a NuxtJS package that allows you to easily add Google analytics to your website, to install this on your website use the command

npm install --save-dev @nuxtjs/google-analytics
{
buildModules: [
'@nuxtjs/google-analytics'
],
}
googleAnalytics: { id: 'UA-XXX-X' }

Add RSS Feed

A common way blogs are discovered and read are via RSS feeds we can add this feature to our blog by using another NuxtJS package we can install by using.

npm install @nuxtjs/feed
feed() {
const baseUrlArticles = 'https://website.com'
const baseLinkFeedArticles = '/feed'
const feedFormats = {
rss: { type: 'rss2', file: 'rss.xml' },
json: { type: 'json1', file: '' },
}
const { $content } = require('@nuxt/content')

const createFeedArticles = async function (feed) {
feed.options = {
title: 'Blog RSS',
description: 'RSS feed',
link: baseUrlArticles,
}
const articles = await $content('articles')
.sortBy('createdAt', 'desc')
.limit(20)
.fetch()

articles.forEach((article) => {
const url = `${baseUrlArticles}/${article.slug}`

const postDate = new Date(article?.createdAt)
feed.addItem({
title: article.title,
id: url,
link: url,
date: postDate,
description: article.description,
content: article.bodyPlainText,
})
})
}

return Object.values(feedFormats).map(({ file, type }) => ({
path: `${baseLinkFeedArticles}/${file}`,
cacheTime: 1000 * 60 * 15,
type,
create: createFeedArticles,
}))
},

Optimize CSS Builds With PurgeCSS

Purge CSS will look at your unused CSS classes and remove them from the final CSS file there is a package that will allow you to easily add this feature into your Nuxt application.

npm install --save-dev nuxt-purgecss
buildModules: [ 'nuxt-purgecss', ],
build: { extractCSS: true, },

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store