Simple steps for successful SEO with Nuxt 2
In this post I explain how to successfully implement SEO aspects like OGP, JSON-LD and good HTML structure technically in Nuxt.js.
This article is about SEO in Nuxt 2. If you are using Nuxt 3 you can read the article Ultimate SEO Guide for Nuxt 3.
Basics
Title & Description
The minimum requirement for search engine optimization is probably the title and description of each page. These two pieces of information are displayed, for example, when you do a Google search for your website.
In Nuxt, this can be easily set in the head
method of one of your pages.
<script>
export default {
head() {
return {
title: 'Creative Websites | Digital Solutions | Online Marketing',
meta: [
{
hid: 'description',
name: 'description',
content: 'I offer your business digital solutions ...'
}
]
};
}
}
</script>
Note that the title and description should contain important keywords and only a certain number of characters for best results. However, this post will only look at the technical implementation.
Title variable
If you want to present your initials as a constant in the title of each page, you can specify this in titleTemplate
of the head property inside your Nuxt configuration.
export default {
head: {
titleTemplate: '%s - thenextbit'
}
};
The %s
is a placeholder for the title that you specify in the head
method of a page, and will be automatically replaced with the title of the page.
Language
You should always specify which language your page is in. If your site has only one language, you can set it globally in Nuxt configuration. You just need to specify the lang
property inside the HTML attributes.
export default {
head: {
htmlAttrs: {
lang: 'en'
}
}
};
If your website is multilingual, you should set the language dynamically. For example, if you are using the i18n Nuxt module, you could set the following code in your default.vue
layout.
<script>
export default {
head() {
return {
htmlAttrs: {
lang: this.$i18n.locale
}
};
}
};
</script>
Canonical
The canonical tag describes where (URL) the original content of your page is located and should be set for each page. Search engines will consider the specified URL with increased relevance.
<script>
export default {
head() {
return {
link: [
{
hid: 'canonical',
rel: 'canonical',
href: 'https://thenextbit.de/'
}
]
};
}
};
</script>
OGP - Open Graph protocol
OGP is an important part through which you store specific metadata for each page that describes a page. This information allows users who share your page on social media, for example, to get a detailed preview.
OGP can be implemented with <meta>
tags. For this, we again use the head
method of a page.
You should specify at least the title, type, image, and the URL (for OGP) of the page. You can of course specify other properties like a description or the language. For more in depth information, read further on the Open Graph protocol website.
<script>
export default {
head() {
return {
meta: [
{
hid: 'og:title',
property: 'og:title',
content: 'Creative Websites | Digital Solutions | Online Marketing'
},
{
hid: 'og:description',
property: 'og:description',
content: 'I offer your business digital solutions ...'
},
{
hid: 'og:type',
property: 'og:type',
content: 'website',
},
{
hid: 'og:image',
property: 'og:image',
content: 'https://thenextbit.de/seo/og_image.jpg'
},
{
hid: 'og:url',
property: 'og:url',
content: 'https://thenextbit.de/'
},
{
property: 'og:locale',
content: 'de_DE'
}
]
};
}
};
</script>
JSON-LD
JSON-LD is a standardized format for writing structured data. If done correctly, search engines like Google can understand what content your website has.
Setup
You can use the nuxt-jsonld module to manage JSON-LD in Vue components.
As this guide is for Nuxt 2, we will use nuxt-jsonld@v1
in the following example. If you are using Nuxt 3, then just install nuxt-jsonld
.
npm install nuxt-jsonld@v1
Create a matching plugin named jsonld.js
in your plugins directory. Don't forget to include the plugin in the Nuxt configuration as well.
import Vue from 'vue';
import NuxtJsonld from 'nuxt-jsonld';
Vue.use(NuxtJsonld);
export default {
plugins: [
'@/plugins/jsonld.js'
]
};
Now the jsonld
method is available to you in all of your pages.
Implementation & results
To better understand the concept, let me show you what the structured data looks like for one of my blog posts. We have a jsonld
method that has various entries.
<script>
export default {
jsonld() {
return {
'@context': 'https://schema.org',
'@type': 'NewsArticle',
mainEntityOfPage: {
'@type': 'WebPage',
'@id': 'https://thenextbit.de/blog/...'
},
headline: '...',
description: '...',
image: '...',
author: {
'@type': 'Person',
name: '...'
},
datePublished: '...',
dateModified: '...',
publisher: {
'@type': 'Organization',
name: 'thenextbit',
logo: {
'@type': 'ImageObject',
url: 'https://thenextbit.de/logo/thenextbit.png'
}
}
};
}
};
</script>
The search engine can then interpret the specified information.
You can test your structured data at any time with this tool from Google.
On Schma.org, you can see all the different types and parameters that are available. You will get, if you structure your data, advanced and visually appealing search engine benefits like in the following image.
HTML
Headline hierarchy
Search engines try to understand your website. It is advisable to establish a well-structured heading structure. For example, each page should only have one <h1>
element.
But you don't have to check the structure manually in your HTML. There are tools for this. If you use Google Chrome, you could use META SEO inspector or something similar.
Links
Links should always contain a text and the relation attribute. Search engines try to understand these links. Include this information with all links.
The list of possible rel
values can be seen in this MDN article.
<nuxt-link to="/blog" rel="next">
All articles
</nuxt-link>
<a href="mailto:info@thenextbit.de" rel="help">
info@thenextbit.de
</a>
Images
For images, it is similar to links, only with different attributes.
Each image should contain an alternative text alt that will be displayed in case the image fails to load.
The
title
is also helpful for accessibility and understanding the context by search engines.The
loading
attribute specifies when the image should load. Suppose an image is far outside the visibility area - then the image does not necessarily have to be loaded. In this case, the value can belazy
. The image will load only when you approach it. If you want the image to load immediately, specify the valueeager
.
<img
src="/services/websites.png"
alt="Brillant and intuitive Websites"
title="Brillant and intuitive Websites"
loading="lazy"
/>
Sitemap
Every website should have a sitemap. The sitemap allows your website to be indexed by search engines. It contains all the subpages of the website that you want to show as well. Mostly, the sitemap is called sitemap.xml
and is addressable in the main directory of a page.
Setup
You can use the Nuxt Sitemap Module.
npm i -D @nuxtjs/sitemap
Add it to the modules array inside your Nuxt configuration.
export default {
modules: [
'@nuxtjs/sitemap'
]
};
Now, a sitemap should be already created automatically for all your static pages. You can view it at /sitemap.xml
.
Configuration
You can configure your sitemap, for example, by specifying pages that should not be indexed with the exclude
field.
Furthermore, you could also specify dynamic routes (e.g., your blog) in the routes
field.
export default {
sitemap: {
hostname: 'https://thenextbit.de/',
gzip: true,
exclude: ['/non-indexed-page'],
// The implementation, how the routes are obtained is up to you
routes: async () => {
const routes = [];
const posts = await getPosts();
posts.forEach((post) => {
routes.push('/blog/' + post.uid);
});
return routes;
}
}
};
The routes
parameter follows the same way as inside the generate property.
Specifying routes for @nuxtjs/sitemap
is optional. Your dynamic routes can be set inside the generate.routes
instead of sitemap.routes
property in the Nuxt configuration (the same way as above). The routes will be automatically recognized by @nuxtjs/sitemap
.
Favicon
A successful website should also show a logo on all devices. Whether in the tabs in the web browser on the desktop or as a bookmark on the homescreen of a smartphone - you should consider all forms.
For this purpose, there is a really handy tool called realfavicongenerator. You can upload your logo, and it will help you convert it to all formats. After that, you can download the favicons and paste them into your ~/static/
directory.
The only thing left for you to do is to include the favicons and metadata in the Nuxt configuration. This is also explained to you step by step by realfavicongenerator.
This is how it could look like:
export default {
head: {
meta: [
{
name: 'msapplication-TileColor',
content: '#ffffff'
},
{
name: 'theme-color',
content: '#ffffff'
}
],
link: [
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: '/favicon-16x16.png'
},
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: '/favicon-32x32.png'
},
{
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/apple-touch-icon.png'
},
{
rel: 'mask-icon',
href: '/safari-pinned-tab.svg',
color: '#581bdf'
},
{
rel: 'manifest',
href: '/site.webmanifest'
}
]
}
};
Conclusion
I hope you got a good overview of the different ways to implement SEO with Nuxt.js.
Update: in the following post Advanced SEO-Workflow with Nuxt.js, you can learn more advanced concepts for SEO with Nuxt.
Recommendations
- Nuxt
- Mixins
- SEO
- Oct 18, 2021
Advanced SEO-Workflow with Nuxt 2
Learn how to improve your Nuxt SEO-Workflow using Mixins and the "Don’t repeat yourself" principle.
- SEO
- Nuxt
- Dec 26, 2022
Ultimate SEO Guide for Nuxt 3
A comprehensive guide to successful technical search engine optimization with Nuxt 3. Learn topics such as HTML basics, JSON-LD, OGP and Sitemap generation.