Creating an S.E.O Conquering Meta Tags Handler in Your Vue App.
Tags: vue, webdev, javascript, seo
Published: 2020-11-30 14:09:49

---
title: Creating an S.E.O Conquering Meta Tags Handler in Your Vue App.
published: true
description: Creating a meta tags handler for a Vue app and perfecting SEO optimization while doing it.
tags: vue, webdev, javascript, SEO
canonical_url: https://jamesinkala.com/blog/creating-an-seo-conquering-meta-tags-handler-in-your-vue-app/
---

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](https://dev-to-uploads.s3.amazonaws.com/i/mvaosg6dqtoh4opg9oyq.gif)

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](https://cli.vuejs.org/guide/creating-a-project.html) and adding views to it. Here we'll add a home and about page if they don't exist yet.
```sh
  $ 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](https://ogp.me/) 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](https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup) 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.
```sh
$ 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.
```js
// 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.

```js
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.
```js
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.
```js
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.
```js
// 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.
```jsx
// 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.
```jsx
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.