Dynamic meta tags in Nuxt application

1.4k Views Asked by At

I need help with dynamic metatags in Nuxt application. I have dynamic component, like this:

```
<template>
  <div>
    <QcLoader v-if="$fetchState.pending" />
    <heroSection :blog="blog" />
    <remoteSection :blog="blog" />
    <readmoreSection :more-blogs="moreBlogs" />
  </div>
</template>
<script>

    import { BLOCKS } from '@contentful/rich-text-types'
    import { documentToHtmlString } from '@contentful/rich-text-html-renderer'
    import client from '@/plugins/contentful'
    export default {
      name: 'Blogid',
      components: {
        QcLoader: () => import('@/components/partials/QcLoader.vue'),
        heroSection: () =>
          import('@/components/sections/company/blog/post/heroSection.vue'),
        remoteSection: () =>
          import('@/components/sections/company/blog/post/remoteSection.vue'),
        readmoreSection: () =>
          import('@/components/sections/company/blog/post/readmoreSection.vue'),
      },
      data() {
        return {
          blog: [],
          moreBlogs: [],
          metadataObject: {}
        }
      },
      fetchDelay: 1000,
       fetch() {
         client
          .getEntries({
            content_type: 'blogPost',
            'fields.slug[in]': this.$route.params.blog,
          })
          .then((entry) => {
            console.log("::: BLOG ENTRY :::", entry)
            console.log(this.$router.getRoutes())
    
            entry.items.forEach(blog => {
    
              this.metadataObject = {
                title: blog.fields.title,
                description: blog.fields.firstTestField.content[0].content[0].value || blog.fields.firstTestField.content[0].content[0].content[0].value,
                url: `https://qedcode.io/company/blog/${blog.fields.slug}`,
                imageUrl: blog.fields.image.fields.file.url,
                imageWidth: blog.fields.image.fields.file.details.image.width,
                imageHeight: blog.fields.image.fields.file.details.image.height,
              }
            })
            
            console.log("::: METADATA :::", this.metadataObject)
    
            this.blog = entry.items[0]
            const rawRichTextField = entry.items[0].fields.firstTestField
            const options = {
              renderNode: {
                [BLOCKS.EMBEDDED_ASSET]: (node) => {
                  const file = node.data.target.fields.file
                  if (
                    file.contentType === 'image/jpeg' ||
                    file.contentType === 'image/png'
                  ) {
                    return '<img src=' + file.url + '>'
                  }
                },
              },
            }
            return documentToHtmlString(rawRichTextField, options)
          })
          .then((renderedHtml) => {
            document.getElementById('blog-html').innerHTML = renderedHtml
          })
          .catch((err) => {
            return err
      })
    client
      .getEntries({
        content_type: 'blogPost',
        order: '-sys.createdAt',
      })
      .then((res) => {
        this.moreBlogs = res.items
      })
      .catch((err) => {
        this.moreBlogs = []
        return err
      })
  },
  head() {
    return {
      title: this.metadataObject.title,
      titleTemplate: '%s',
      meta: [
        {
          name: 'description',
          hid: 'description',
          content: this.metadataObject.title,
        },
        /* Open Graph */
        {
          name: 'og:title',
          hid: 'title',
          content: this.metadataObject.title,
        },
        {
          name: 'og:site_name',
          hid: 'site-name',
          content: 'QED',
        },
        {
          name: 'og:url',
          hid: 'url',
          content: this.metadataObject.url,
        },
        {
          name: 'og:description',
          hid: 'description',
          content: this.metadataObject.description,
        },
        {
          name: 'og:type',
          hid: 'type',
          content: 'website',
        },
        {
          name: 'og:image',
          hid: 'image',
          content: this.metadataObject.imageUrl,
        },
        {
          name: 'og:image:width',
          hid: 'image-width',
          content: this.metadataObject.imageWidth,
        },
        {
          name: 'og:image:height',
          hid: 'image-height',
          content: this.metadataObject.imageHeight,
        },
        /* Twitter */
        { name: 'twitter:card', content: 'summary_large_image' },
        { name: 'twitter:site', content: '@QED_code' },
        {
          name: 'twitter:title',
          content: this.metadataObject.title,
        },
        {
          name: 'twitter:description',
          content: this.metadataObject.description,
        },
        { name: 'twitter:url', content: this.metadataObject.url },
        {
          name: 'twitter:image',
          content: this.metadataObject.imageUrl,
        },
      ],
      link: [
        {
          rel: 'canonical',
          herf: 'https://qedcode.io',
        },
      ],
    }
  },
  fetchOnServer: false,
}
</script>
```

The problem is that when I click on "View Page source", the metatags are undefined. My guess is that fetch() method executes after head() method.

This is Page source after code is executed (you can see that all values are undefined): Page source

The component is type of blog, so it's dynamic and for each blog I need different metatags.

When I click on each blog, the metadataObject fills that data property like this: metadataObject

1

There are 1 best solutions below

0
Hvitis On

By default ssr property is switched ON in your nuxt.config.js which enabled crawlers to recieve a page from the server (instead of just a JS package that is impossible to crawl for meta tags etc).

This forces you to use asyncData due to the nuxtJS lifecycle. You can still access most of the app's information that you would need for your meta.

async asyncData({ isDev, route, store, env, params, query, req, res, redirect, error }) {

return { yourAsyncData }

}