Adding dynamic meta tags fetched from API to nuxtjs static site - vue.js

I have a static site with Nuxt and content coming in from Strapi. I want to dynamically set the meta tags that are fetched asynchronously.
My site has an index page which passes the fetched data to index-web or index-mobile through props.
let pageMeta: any
const apiBase: string = 'https://strapi.xyz.com'
export default Vue.extend({
components: { Greeting, Showcase, Features, Footer },
props: {
data: Map,
pageMeta,
},
data() {
return {
loading: true,
}
},
metaInfo(): any {
return {
meta: [
{
hid: 'description',
name: 'description',
content: pageMeta.description,
},
{
hid: 'author',
name: 'author',
content: pageMeta.author,
},
],
}
},
})
The prop being passed in a JSON parsed object.
Having done this, the generated site does not have the meta tags added in.

As mentioned, you need to access the property with .this.

Related

vuejs3, cannot add dynamic title and description with usehead meta package

I have been trying to use Vuejs3 useHead package and add dynamic title and decription meta, but i get $route is undefined.... i need help...
<router-link :to="{name:'productDetails', params:{id:rope._id, title:rope.title, price: rope.price, description:rope.description, quantity:rope.totalQuantity}}">
peoductDetails.vue:
setup(){
useHead({
title: this.$route.params.title,
meta: [
{
name: `description`,
content: `blalala`
},
],
})
},
You need to use useRoute() in order to access the router, since in the setup hook this does not refer to the component instance.
setup(){
const route = useRoute();
useHead({
title: route.params.title,
meta: [
{
name: `description`,
content: `blalala`
},
],
})
},

Vue Server Side Rendering and Twitter Cards

Anybody have experience with twitter cards and Vue, dynamically loading meta tags into the components? seems like it needs server side rendering, does anybody know a way to avoid coding all the server side rendering? I am loading the meta tag information from APIs in Vuex and using them within my vue-head meta function:
meta: function() {
return [
//twitter
{ name: "twitter:title", content: this.title, id: "t-title" },
{ name: "twitter:image", content: this.image, id: "t-image" },
{ name: "twitter:description", content: this.excerpt, id: "t-excerpt" },
{
name: "twitter:card",
content: "summary_large_image",
id: "t-card",
},
// Facebook / Open Graph
{ property: "og:title", content: this.title, id: "og-title" },
// with shorthand
{ p: "og:image", c: this.image, id: "og-image" },
];
},

vue-meta doesnt display title, content, or schema on page refresh or external link click for dynamic components

When I navigate to my dynamic components trough navigation bar, vue-meta title, content, and schema are displayed correctly, but when I refresh the page or click on the external link I get a value of undefined.
i have stored title content and schema in the json file.
metaInfo() {
return {
title: `${this.seoTitle}`,
meta: [
{name: "robots", content: "index,follow"},
{
name: 'description',
content: `${this.seoContent}`
}
],
link: [
{rel: 'favicon', href: 'logo.ico'}
],
script: [{
type: 'application/ld+json',
json: this.markups
}]
}
},
data() {
return {
seoTitle: this.$route.params.title,
seoContent: this.$route.params.content,
markups:this.$route.params.markup,
}
}
<div class="landing-group-tours box" v-for="tour in boatTours" :key="tour.id">
<router-link
:to="{name: 'details', params:{id: tour.id, title: tour.seoTitle, content: tour.seoContent, markup:tour.markup}}">
</div>
<script>
import tours from '#/data/privateToursData.json'
export default{
data(){
return{
boatTours: tours
{
}
}
</script>
You should store the router parameters in the router itself or in the URL, not the link. What you do is passing objects internally when you click the link, but as you noticed, when you click the browser refresh button these params are gone.
What happens is that Vue will load the app and router, identify what components are responsible for rendering the route and pass the detected parameters from your router to the components. Hence losing any additional data you had in your link before.
Try to keep only the dynamic params in your router and load the rest in the component, based on app logic. I.e. Assuming your route looks like /details/:id, you should initialize the SEO params in your details component.
Typically these come from the backend and for faster access I would transform the array to literal object and access the record by key. I.e. transform the array from:
[
{ "id": 1008; "title": "title1", "content": "....", ... },
{ "id": 1009, "title": "..."... }
]
to
{
"1008": { title: "title1", content: "....", ... },
"1009": { .... }
}
and then store it in VueX (https://vuex.vuejs.org/guide/getters.html)
getters: {
// ...
getBoatTour: (state) => (id) => {
return state.boatTours[id] || { title: 'Not found', content: '......' }
}
}
data() {
const SEOdata = store.getters.getBoatTour(this.$route.params.id);
return {
seoTitle: SEOdata.title,
seoContent: SEOdata.content,
markups: SEOdata.markup,
}
}

Nuxt.js after page refresh meta are filled from config instead of head method

I had problem with meta in nuxt.js app. I want to fill dynamic meta tags in one detail page
--pages
----event
-----_id.vue
When I navigate on web site via link all work great. But if I just refresh page, meta tags use value from nuxt.config.js. For instance I got 'SiteTitle Nuxt.config.js' instead of 'SiteTitle - Some event title'.
Nuxt version 2.15.3
nuxt.config.js
export default {
head: {
titleTemplate: '%s - SiteTitle',
title: 'SiteTitle Nuxt.config.js',
htmlAttrs: {
lang: 'en'
},
meta: [
{charset: 'utf-8'},
{name: 'viewport', content: 'width=device-width, initial-scale=1'},
{hid: 'description', name: 'description', content: ''}
],
link: [
{rel: 'icon', type: 'image/x-icon', href: '/favicon.ico'}
]
}
components: true,
buildModules: [
'#nuxt/typescript-build',
'#nuxtjs/vuetify',
],
modules: [`enter code here`
'#nuxtjs/axios'
],
vuetify: {
customVariables: ['~/assets/variables.scss'],
},
axios: {
baseURL: 'https://api.someurl.com',
}
}
And _id.vue file
<template>
<v-card class="mt-6 mb-5" outlined>
<v-card-title>{{ model.title }}</v-card-title>
</v-card>
</template>
<script lang="ts">
import {Component, Vue} from "nuxt-property-decorator"
import {EventModel} from "~/api/models/EventModel";
import EventApi from "~/api/EventApi";
#Component({
async asyncData({params, $axios}) {
const eventApi = new EventApi($axios)
const model = await eventApi.get(parseInt(params.id))
return { model: model };
},
head(this: EventPage): object {
return {
title: "SiteTitle - " + this.model.title,
meta: [
{
hid: 'description',
name: 'description',
content: this.model.shortDescription
}
]
}
},
})
export default class EventPage extends Vue {
model = {} as EventModel
async fetch() {
}
}
</script>
I tried to call api in fetch, in this case meta values always have valid value when I refresh page, but Facebook Sharing Debugger get meta from nuxt.config.js in this case, and this solution is not suitable
Thanks for your help
You can do one thing
Create one plugin in that you can use this on router.beforeEach like this:
app.router.beforeEach(async(to, _, next) => {
document.title = to.meta.pageTitle ? ('Specify title - ' + ${to.meta.pageTitle}) :'Default Title';
})
In your router file you can do something like this:
{
path: '/path',
name: 'Name',
component: Component,
meta: {
pageTitle: 'Change Password'
}
}

Alternate page with proper canonical tag and duplicate URL part errors in Google search console report

I have a static blog site created with Nuxt and hosted on Netlify. For content, I am using markdown files and the new Content module in Nuxt. I used Nuxt and static content, along with vue-meta for SEO purposes, but after a couple months, my coverage report shows multiple errors, and I can't get any pages returned in Google, even when searching on the site name or a specific string of text used on the site.
Here's my setup:
My markdown content is stored in /content/posts
In my blog/index.vue file, this is what I have for my meta tags and data fetching:
<script>
export default {
name: 'Posts',
head() {
return {
title: 'Blog Posts',
meta: [
{
hid: 'description',
name: 'description',
content: 'Example Site blog index'
},
{
hid: 'link',
name: 'link',
content: 'https://example.com/blog'
}
]
}
},
async asyncData({ $content, params }) {
const posts = await $content('posts', params.slug)
.only(['title', 'description', 'publishDate', 'slug'])
.sortBy('publishDate', 'desc')
.fetch()
return {
posts
}
},
};
</script>
In my /blog/_slug/index.vue page, I'm doing almost the same thing.
export default {
name: 'Post',
computed: {
pageConfig() {
return {
identifier: this.post.slug
}
}
},
head() {
return {
title: this.post.title,
meta: [
{
hid: 'description',
name: 'description',
content: this.post.description
},
{
property: "og:site_name",
hid: "og:site_name",
vmid: "og:site_name",
content: "My Site Name"
},
{
property: "og:title",
hid: "og-title",
vmid: "og-title",
content: this.post.title
},
{
property: "og:description",
hid: "og-description",
vmid: "og-description",
content: this.post.description
},
{
property: "og:type",
hid: "og-type",
vmid: "og-type",
content: "article"
},
{
property: "og:url",
hid: "og-url",
vmid: "og-url",
content: 'https://example.com/blog/' + this.post.slug
},
{
hid: "link",
name: "link",
content: 'https://example.com/blog/' + this.post.slug
},
],
link: [
{
rel: 'canonical',
href: 'https://example.com/blog/' + this.post.slug
}
]
}
},
async asyncData({ $content, params }) {
const post = await $content('posts', params.slug).fetch()
return {
post
}
},
}
I'm using this to generate my sitemap in nuxt.config.js
sitemap: {
path: '/sitemap.xml',
hostname: process.env.BASE_URL,
gzip: true,
routes: async () => {
let routes = []
const { $content } = require('#nuxt/content')
let posts = await $content('posts').fetch()
for (const post of posts) {
routes.push(`blog/${post.slug}`)
}
return routes
},
}
and this generates a sitemap at /sitemap.xml with the following structure
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://example.com/blog/blog-post-1</loc>
</url>
<url>
<loc>https://example.com/blog/blog-post-2</loc>
</url>
<url>
<loc>https://example.com/blog/blog-post-3</loc>
</url>
</urlset>
I submitted my sitemap to Google.
When I look at my Coverage report in Google Search Console, I have two main problems:
A large number of posts listed under Not Found (404) with a URL of https://example.com/blog/blog/my-blog-post (two /blogs in the URL). I've noticed this happens sometimes when I click on a post right after deploying to Netlify. I've looked around the code, and I can't find where I might be adding an extra /blog in the URL.
An equally large number under Alternate page with proper canonical tag. Before I put the canonical tag in my /_slug/index/vue header, I got errors about missing a canonical tag, and now that I put that in, I get this error instead.
What do I need to do to fix these errors and get my content correctly indexed?