We have a webpage built with vue.js and nuxt. We use nuxt generate
to generate static html files, which also should contain the SEO and og tags.
That's why we use nuxt's head ()
to generate the meta info.
So far so good.
But now we have a page that asynchronously loads a post into a nested route. If you go to that page, it loads the post's data via ajax call. Then it would use the post's meta to populate the <head>
.
The meta information is updated correctly after some time (after the post is loaded) BUT when we use nuxt generate
the post's data and therefore it's meta info is not present at the time when we generate the meta information with head ()
.
That's why our static html does not contain the necessary meta info. What would be possible solutions for this? Ideal would be that the generation process waits for the post to be loaded. Could it be solved with promises? Or are there some other ideas?
Here this.post
is set to false first.
our helper function generateMetaInfo is called but obviously does not have the correct data.
head () {
this.log('this.post: ', this.post)
if (this.post) {
return generateMetaInfo(this.post)
}
}
We load the post like this, when calling the url:
getPost () {
// Only if postSlug is present
if (this.$route.params.postSlug) {
// If postslug > try to get it from store
if (this.getCompletePostBySlug(this.$route.params.postSlug)) {
this.activePost = this.getCompletePostBySlug(this.$route.params.postSlug)
}
// if it is not in store get it via axios
else {
const ax = axios.create({
baseURL: `${window.location.origin}/${this._checkRouteByString('programm') ? 'events' : 'posts'}`
})
ax.get(`posts.${this.$i18n.locale}.${this.$route.params.postSlug}.json`)
.then((response) => {
const newActivePost = response.data && response.data.items ? response.data.items.find(p => p.slug === this.$route.params.postSlug) : false
if (newActivePost) {
this.post = newActivePost
this.$store.dispatch('data/saveCompletePosts', this.activePost)
} else {
this.post = '404'
}
})
.catch((error) => {
// console.log(error.response)
})
}
} else {
this.setActivePost()
}
},
So we would have to wait for the ajax call to be finished.
Any idea that could help us find a solution is very much appreciated.
Cheers
============================
EDIT:
Using a promise did not work either:
methods: {
getPostMeta: async function () {
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = {
title: 'Promise Title Test',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'keywords', content: 'keyword 1, keyword 2'},
{ hid: 'description', name: 'description', content: 'PROMISE. This is the generic promise descr.'}
]
}
resolve(result)
}, 1000)
})
let result = await promise
console.log('result: ', result)
return result
}
},
head () {
return this.getPostMeta()
}
This would not wait until the promise is resolved... :( (of course this was only an example with a timeout, in the real world this would have to be exchanged with an ajax call, getting the post's data)
Nuxt is an awesome framework, however SEO and assigning meta tags on dynamic routes is quite difficult. It's probably one of the very few reasons why it's not so good.
I think the main issue here is that you're trying to load meta data from a method that is only called once the promise or function has resolved, which means that it's never going to get the metadata until after the page has rendered. I wouldn't use functions or promises for this.
Essentially, the way I've found to get around this problem is that you need to load all of the meta data (coupled with the name of your post as an id or something) in the format
posts: [
{
slug: 'some_id',
meta: [
{
name: 'title',
content: 'some_title'
},
{
...
]
},
{
...
for your dynamic pages into one array - I'm using the VueX Store to do this, for example - and then you can use a statement such as
this.$store.state.array.find(p => p.slug === this.$route.params.postSlug).meta
in your .vue file where id is whatever value you want to compare with the route parameter. And this will return the array of metadata.
I realize that this is somewhat inefficient and most people would probably cringe at the thought of it, but it seems to work really well for dynamic routes.
My head() method looks like this
head() {
return {
title: "Title " + this.$route.params.id,
meta: this.$store.state.results.find((result) => result.id === this.$route.params.id).meta
}
}
This works perfectly for me. If you do try this, I'd be interested to hear how it goes.
Cheers
Code explanation
export default {
async asyncData({$content, params}) {
const page = await $content('articles/some-page').fetch()
return {
page: page
}
},
head() {
// remember page is in function asyncData and it had a key title
return {
title: 'Articles: ' + this.page.title
}
}
}
Will set the page title to "Article + whatever the value is in this.page.title"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With