Tutorial

Handling Metadata in Vue with vue-meta

Vue.js

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

The vue-meta library provides a Vue plugin that allows us to take control of our app’s metadata from a component level. It’s important to curate the metadata of our web apps for SEO, but when working with single-page web applications (SPAs) this can often be a cumbersome task.

Dynamic metadata was already partially covered here, but our goal today is to highlight how the vue-meta plugin handles this for us in a concise, logical way while providing us with even more control over our app’s metadata.

Setup

Since vue-meta is a plugin we’ll need to add the package to our project dependencies. We’ll also need to let Vue know we want to use the vue-meta plugin.

Installation

Install vue-meta with your preferred package manager:

# Yarn
$ yarn add vue-meta
# NPM
$ npm install vue-meta --save

Bootstrap

Bootstrap the vue-meta plugin in your main JavaScript file:

main.js
import Vue from 'vue';
import VueMeta from 'vue-meta';

import App from 'App.vue';

Vue.use(VueMeta);

new Vue({
  el: '#app',
  render: h => h(App)
});

If you’re using a routing solution like Vue Router, then you could bootstrap vue-meta in your router.js file:

router.js
import Vue from 'vue';
import Router from 'vue-router';
import VueMeta from 'vue-meta';

Vue.use(Router);
Vue.use(VueMeta);

export default new Router({})

SSR

If you’re using Server Side Rendering you’ll want to bootstrap vue-meta in a file that runs on both the server and the client before the root Vue instance is mounted.

Vue Frameworks

If you’re using a framework that already uses vue-meta, such as NuxtJS, you won’t need to bootstrap. Instead, you should refer to the documentation for your chosen framework. Other frameworks that already use vue-meta include Gridsome, Ream, Vue-Storefront, and Factor.

Plugin Options

vue-meta provides options to customize the plugin’s behavior. NuxtJS takes advantage of this by changing the name of the metaInfo property to head. You could do this by bootstrapping vue-meta like so:

import Vue from 'vue';
import VueMeta from 'vue-meta';

import App from 'App.vue';

Vue.use(VueMeta, {
  keyName: 'head'
});

new Vue({
  el: '#app',
  render: h => h(App)
});

Make sure to check out the full list of options available in the official documentation.

Populating Metadata

Titles

vue-meta allows us to update the <title> tag on both parent and child components. In our root component we can define a default title that will appear if a child component lacks one. We can also define a titleTemplate which will be used to display the title from child components.

App.vue
export default {
  name: 'App',
  metaInfo: {
    title: 'Default App Title',
    titleTemplate: '%s | vue-meta Example App'
  },
  ...
}
<title>
  Default App Title | vue-meta Example App
</title>

Other Metadata

Of course, title isn’t the only thing we care about when populating a page’s metadata. Often we want to include other information to pass to the browser or web crawler such as a page’s charset, description, or viewport. You can even add attributes to the page’s html or head tags and inject external scripts.

export default {
  name: 'App',
  metaInfo: {
    title: 'Default App Title',
    titleTemplate: '%s | vue-meta Example App',
    htmlAttrs: {
      reptilian: 'gator'
    },
    headAttrs: {
      nest: 'eggs'
    },
    meta: [
      { charset: 'utf-8' },
      { name: 'description', content: 'gator' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' }
    ]
  },
  ...
}
<html reptilian="gator">
  <head nest="eggs">
    <title>Default App Title | vue-meta Example App</title>
    <meta charset="utf-8">
    <meta name="description" content="gator">
    <meta name="viewport" content="width=device-width, initial-scale=1">
  </head>
</html>

Make sure to check out the metaInfo properties spec of the vue-meta API documentation for all of the options available.

Component Metadata Hierarchy

Child components will recursively merge metadata with their parents. This allows us to update the page’s metadata based on which components are currently mounted.

App.vue
<template>
  <div>
    <HelloWorld />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';
export default {
  name: 'App',
  metaInfo: {
    title: 'Default App Title',
    titleTemplate: '%s | vue-meta Example App'
  },
  components: {
    HelloWorld
  }
}
</script>
HelloWorld.vue
<template>
  <div>Hello World!</div>
</template>

<script>
export default {
  name: 'HelloWorld',
  metaInfo: {
    title: 'Hello World!'
  }
}
</script>
<title>
  Hello World! | vue-meta Example App
</title>

You could also disable the titleTemplate from a child component like so:

HelloWorld.vue
export default {
  name: 'HelloWorld',
  metaInfo: {
    title: 'Hello World!',
    titleTemplate: null
  }
}
<title>
  Hello World!
</title>

If two child components are mounted and both contain metaInfo, the last child to be mounted will be used to populate the page’s metadata. Suppose we created a second child component called HelloWorld2 and modified our example as below:

Template: App.vue
<template>
  <div>
    <HelloWorld />
    <HelloWorld2 />
  </div>
</template>
Script: App.vue
import HelloWorld from './components/HelloWorld.vue';
import HelloWorld2 from './components/HelloWorld2.vue';
export default {
  name: 'App',
  metaInfo: {
    title: 'Default App Title',
    titleTemplate: '%s | vue-meta Example App'
  },
  components: {
    HelloWorld,
    HelloWorld2
  }
}
HelloWorld2.vue
<template>
  <div>Hello World 2!</div>
</template>

<script>
export default {
  name: 'HelloWorld2',
  metaInfo: {
    title: 'Hello World 2!'
  }
}
</script>
<title>
  Hello World 2! | vue-meta Example App
</title>

Only duplicate metadata will be overwritten by child components. Other metadata will be concatenated.

Using multiple Vue instances with vue-meta will result in only the metadata from the last app to be updated!

VMID

vue-meta allows us to assign a special property called vmid to our metaInfo so that we can control how it resolves with our component tree. If two sets of metadata have the same vmid, such as a parent and child, they will not merge but instead the child will override the parent like so:

// parent component
{
  metaInfo: {
    meta: [
      { charset: 'utf-8' },
      { vmid: 'description', name: 'description', content: 'reptilian' }
    ]
  }
}
// child component
{
  metaInfo: {
    meta: [
      { vmid: 'description', name: 'description', content: 'gator' }
    ]
  }
}
<meta charset="utf-8">
<meta data-vmid="description" name="description" content="gator">

Remove parent property in child

If a child component shares a vmid with a parent and a metaInfo property is set to null, this property will be removed from the parent.

Conditional property in child

If a child component returns undefined for a metaInfo property vue-meta will fall back to the parent’s property.

Wrapping Up

vue-meta is a great solution if you’re looking to take control of and dynamically update your app’s metadata. It’s no question why so many popular Vue frameworks include the library out of the box. Make sure to take a look at the official documentation if you’d like to learn more about all the library has to offer.

Creative Commons License