BlogHome
Creating an S.E.O Conquering Meta Tags Handler in Your Vue App.

Creating an S.E.O Conquering Meta Tags Handler in Your Vue App.

Nov 30, 2020Updated on Sep 19, 2021

As I've been working on my personal site for the last couple of days, it came to my attention that it was heavy and needed some chopping down. Finally, I did it and lighthouse is my witness.

Lighthouse Report jamesinkalame

Down this "rabbit hole", I decided to add some meta tags for the sake of S.E.O. One might think, well it's a Vue environment, vue-meta for the go right? Well, not quite (PS: I love vue-meta). But I just got on to a re-inventing-the-wheel kind of state on this last quarter of the year, and also the fact that I got a bit of free time on my hands.

So it was settled, down the 'rabbit hole' I went, and I have the following to show for, baby bunnies 🐇🐇.

Here's how you can replicate the creation of this beast of a meta tags handler.

Start by creating a new Vue project and adding views to it. Here we'll add a home and about page if they don't exist yet.

$ mkdir views
$ cd views
$ echo "<template></template>" > Home.vue
$ echo "<template></template>" > About.vue

The SEO meta tags we are interested in

This is a list of meta tags that we will be implementing in our app:

  • page title : Make sure it is unique and under 60 characters.
  • page description meta tag: Keep it between 50 - 160 characters.
  • Open Graph meta tags: Help your content stand out when getting shared on social feeds.
    • og:image
    • og:type
    • og:title
    • og:description
    • og:site_name
    • og:url
  • Twitter Card meta tags: Help us attach rich photos, videos and media experiences to Tweets.
    • twitter:card
    • twitter:site
    • twitter:creator
    • twitter:title
    • twitter:description
    • twitter:image

Creating our meta tags handler

Start by adding a metas.js file inside our /src directory.

$ echo '// meta handler' > src/metas.js

Inside this file is where most of the meta tags management work is done for the app. First, start by adding the meta content that won't change throughout the site.

// metas.js
let siteName = 'Conquering SEO';
let image = 'https://some-domain.com/site-banner.jpg';
let errorImage = 'https://some-domain.com/404-error-image.jpg';
let twitterHandler = '@handler';
let twitterCard = 'summary';

Next, create an object that will be holding all existing page titles and descriptions.

let titleDescriptionMetas = {
    title: {
        home: 'Coolest home page title here',
        about: 'What this site is all about'
    },
    description: {
        home: 'Page descriptions need to be around the 70 string length limit',
        about: 'About Page description herength (70 words)',
    },
};

After, create an object that will carry page specific data for the remaining meta tags listed above.

const PageData = [
    {
        pageName: 'home',
        title: titleDescriptionMetas.title.home,
        tags: [
            { name: 'description',
                content: titleDescriptionMetas.description.home },
            { name: 'og:image',
                content: image },
            { name: 'og:type',
                content: 'website' },
            { name: 'og:title',
                content: titleDescriptionMetas.title.home },
            { name: 'og:site_name',
                content: siteName },
            { name: 'og:url',
                content: '' },
            { name: 'twitter:card',
                content: twitterCard },
            { name: 'twitter:site',
                content: twitterHandler },
            { name: 'twitter:creator',
                content: twitterHandler },
            { name: 'twitter:title',
                content: titleDescriptionMetas.title.home },
            { name: 'twitter:description',
                content: titleDescriptionMetas.description.home },
            { name: 'twitter:image',
                content: image },
        ]
    },
    {
        pageName: 'about',
        title: titleDescriptionMetas.title.about,
        tags: [
            { name: 'description',
                content: titleDescriptionMetas.description.about},
            { name: 'og:image',
                content: image },
            { name: 'og:type',
                content: 'website' },
            { name: 'og:title',
                content: titleDescriptionMetas.title.about },
            { name: 'og:site_name',
                content: siteName },
            { name: 'og:url',
                content: '' },
            { name: 'twitter:card',
                content: twitterCard },
            { name: 'twitter:site',
                content: twitterHandler },
            { name: 'twitter:creator',
                content: twitterHandler },
            { name: 'twitter:title',
                content: titleDescriptionMetas.title.about },
            { name: 'twitter:description',
                content: titleDescriptionMetas.description.avoutb},
            { name: 'twitter:image',
                content: image },
        ]
    },
};

Finishing off, we export assignMetas function that will be called in our app pages.

export const assignMetas = (pageName, path = window.location.href, injectDynamicContent = false, pageData = null) => {
    if(!injectDynamicContent){ // static pages
        let exist = PageData.filter((x) => x.pageName === pageName);
        if(exist.length > 0){
            document.title = exist[0].title;

            // remove stale metas
            Array.from(document.querySelectorAll('[data-vue-meta-controlled]')).map(el => el.parentNode.removeChild(el));

            exist[0].tags.map(tagDef => {
                let tag = document.createElement('meta')
                let urlHelperVal = false // will help us search for 'og:url'
                Object.keys(tagDef).forEach(key => {
                    tag.setAttribute(key, urlHelperVal ? path : tagDef[key]);
                    urlHelperVal = tagDef[key] === "og:url"
                })
                tag.setAttribute('data-vue-meta-controlled', '')
                return tag;
            }).forEach(tag => document.head.appendChild(tag));
        }
    } else { // dynamic pages (e.g blog post page)
        document.title = pageData.title;

        // remove stale metas
        Array.from(document.querySelectorAll('[data-vue-meta-controlled]')).map(el => el.parentNode.removeChild(el));

        pageData.tags.map(tagDef => {
            let tag = document.createElement('meta')
            let urlHelperVal = false // will help us search for 'og:url'
            Object.keys(tagDef).forEach(key => {
                tag.setAttribute(key, urlHelperVal ? path : tagDef[key]);
                urlHelperVal = tagDef[key] === "og:url"
            })
            tag.setAttribute('data-vue-meta-controlled', '')
            return tag;
        }).forEach(tag => document.head.appendChild(tag));
    }
};

#### A breakdown of the above code: #### For static pages, we get all of the meta tags info from the PageData object, assign the page's title followed by removing all meta tags that have the data-vue-meta-controlled attribute from the page's <head>. We then proceed by creating and adding new meta tags to the page's <head> from the page's specific data we obtain from the earlier PageData object and finalizing this by giving them an extra empty attribute data-vue-meta-controlled, which we will use to identify all of these changeable meta tags.

For dynamic pages, we pass the current page's url and a page specific pageData object as arguments into the assignMetas function proceeding by repeating what we did with the static pages using this dynamically obtained page specific data.

Implementing the assignMetas function inside app views

Make sure to have your route names corresponding to the pageName properties of our pageData object declared in our metas.js file.

// router.js
{
  path: '/',
  name: 'home', // this right here
  component: () => import('./views/Home.vue')
},
{
  path: '/about',
  name: 'about', // this right here
  component: () => import('./views/About.vue')
}
For static pages (pages without dynamic content)

First import the assignMetas function, then pass the route name as the pageName argument when the component is mounted.

// Home.vue
import {assignMetas} from '@/metas'

export default {
  mounted(){
    assignMetas(this.$route.name)
  }
}
For dynamic pages

Same as the implementation above with the only difference being, after the dynamic content has been fetched from an API for instance, we construct a pageData object carrying the data we want for our meta tags from the API response as it's properties, then passing it to assignMetas along with the page's url as path and setting the injectDynamicContent argument to true.

mounted(){
    this.fetchPageData()
},
methods: {
    fetchPageData(){
        fetch('http://api-endpoint')
        .then(res => res.json())
        .then(data => {
            let siteName = 'Conquering SEO';
            let twitterHandler = '@handler';
            let twitterCard = 'summary';
            const pageData = {
                title: `${data.post_title } | ${siteName}`,
                tags: [
                    { name: 'description',
                        content: data.short_description },
                    { name: 'og:image',
                        content: data.post_image },
                    { name: 'og:type',
                        content: 'website' },
                    { name: 'og:title',
                        content: data.post_title },
                    { name: 'og:site_name',
                        content: siteName },
                    { name: 'og:url',
                        content: window.location.href },
                    { name: 'twitter:card',
                        content: twitterCard },
                    { name: 'twitter:site',
                        content: twitterHandler },
                    { name: 'twitter:creator',
                        content: twitterHandler },
                    { name: 'twitter:title',
                        content: data.post_title },
                    { name: 'twitter:description',
                        content: data.short_description },
                    { name: 'twitter:image',
                        content: data.post_image },
                ]
            }
            assignMetas('', window.location.href, true, pageData)
        })
        .catch(error => {
            // deal with the error
        })
    }
}

And that's all for implementing a meta tags handler in Vue.