Issues with Vue meta not receiving updates

My internal pages do not update the vue-meta with new page values when I visit them.

Code

app.js

import VueMeta from 'vue-meta'
Vue.use(VueMeta, {
    refreshOnceOnNavigation: true
})

App.vue (main component)

export default {
  metaInfo() {
    return {
      title: process.env.MIX_APP_NAME,
      titleTemplate: `%s | ${process.env.MIX_APP_NAME}`,
      meta: [
        { name: "robots", content: "index,follow" },
        {
          vmid: "description",
          name: "description",
          content:
            "..........",
        },
        // and many more....
      ],
   }
  }
}

post.vue (internal component)

export default {
  name: "singePost",
  data() {
    return {
      post: "",
    };
},
metaInfo() {
    return {
        title: this.post.name, // not receiving data
        meta: [
            {
                vmid: "description",
                name: "description",
                content: this.post.metas[0].description, // not receiving data
            },
            // others......
        ],
    }
},
mounted() {
    this.getPost();
},
methods: {
    getPost() {
        axios
        .get("/api/articles/" + this.$route.params.slug, {
          headers: {
            Authorization: localStorage.getItem("access_token"),
          },
        })
        .then((response) => {
          this.post = response.data.data;
        })
        .catch((error) => {
            //....
        });
    },
},

Any idea?

Update

I have noticed that my vue-meta updates but with a delay. This delayed update is causing issues for social network websites and SEO checkers to retrieve my URLs accurately.

Clarify

  1. Vue-meta gets updated but late
  2. The delayed update hampers proper presentation of SEO information when links are shared and validated.

My full meta tags code

metaInfo() {
    return {
      title: this.post.name,
      meta: [
        {
          vmid: "keyword",
          name: "keyword",
          content: this.post.metas[0].tags,
        },
        {
          vmid: "description",
          name: "description",
          content: this.post.metas[0].description,
        },
        // Open Graph / Facebook
        { vmid: "og:type", name: "og:type", content: "website" },
        {
          vmid: "og:url",
          name: "og:url",
          content: process.env.MIX_APP_URL + this.$router.currentRoute.fullPath,
        },
        {
          vmid: "og:site_name",
          name: "og:site_name",
          content: `"${process.env.MIX_APP_NAME}"`,
        },
        {
          vmid: "og:title",
          name: "og:title",
          content: this.post.name,
        },
        {
          vmid: "og:description",
          name: "og:description",
          content: this.post.metas[0].description,
        },
        {
          vmid: "og:image",
          name: "og:image",
          content: this.post.imagebig,
        },
        // Twitter
        {
          vmid: "twitter:card",
          name: "twitter:card",
          content: "summary",
        },
        {
          vmid: "twitter:author",
          name: "twitter:author",
          content: "@xxxxxx",
        },
        {
          vmid: "twitter:site",
          name: "twitter:site",
          content: "@xxxxxx",
        },
        {
          vmid: "twitter:creator",
          name: "twitter:creator",
          content: "@xxxxxx",
        },
        {
          vmid: "twitter:url",
          name: "twitter:url",
          content: process.env.MIX_APP_URL + this.$router.currentRoute.fullPath,
        },
        {
          vmid: "twitter:title",
          name: "twitter:title",
          content: this.post.name,
        },
        {
          vmid: "twitter:description",
          name: "twitter:description",
          content: this.post.metas[0].description,
        },
        {
          vmid: "twitter:image",
          name: "twitter:image",
          content: this.post.imagebig,
        },
      ],
    };
},

Extra

  1. I recently read an article stating that due to vue-meta (Vue in general) loading based on JavaScript, social media crawlers cannot cache the information. Hence, the link details cannot be displayed accurately when shared on platforms like FB or Twitter.

  2. The suggested solution was to use Nuxt and return metadata server-side.

Questions

  1. I am uncertain about the accuracy of point #1 above, but it is a possibility.
  2. Although my app does not primarily use Nuxt, I have installed the npm package. It might be worth trying out as a potential solution (however, since I am unfamiliar with Nuxt, additional details in your answer would be very helpful).

Answer №1

Vue is a client-side JavaScript framework. When you create your website, the index.html file only contains JavaScript code that generates content when it is executed. This setup also applies to VueMeta. The issue arises when sharing links on platforms like Facebook and Twitter, as they analyze the content without running any JavaScript, leading to them not detecting meta tags generated by VueMeta...

The workaround for this is to deliver a fully or partially prerendered page with all necessary information without relying on JavaScript execution.

One approach is to utilize Vue server-side rendering, which frameworks like Nuxt employ.

There are two main methods:

1. SSR - the page is rendered when requested by the client or bot. Often requires a Node server, such as with Nuxt.js.

2. SSG - pages are pre-generated at build time, including HTML. No Node server is needed, making it suitable for hosting on CDNs or static hosting services like Netlify. Examples include Gridsome, VuePress, and Nuxt (supports this method too).

Additionally, alternatives like using headless Chrome/Puppeteer or services like Prerender.io exist.

Nuxt

Nuxt is powerful but imposes specific app structures (file-based routing) and data retrieval methods. Transitioning to Nuxt may necessitate rewriting your entire app and running a Node server for hosting.

However, if you are already utilizing Laravel as your server, it might be more efficient to handle meta tag generation directly within Laravel.

UPDATE: It appears possible to integrate Vue SSR directly into Laravel.

Answer №2

Your observations are spot on. I found myself delving deep into this very issue some time ago, and after much research, I devised a solution that worked for me. Here's what I came up with:

  1. Continue using vue-meta for crawlers that execute JavaScript.
  2. Integrate a server-side solution (utilizing a Laravel package).

Option 1 is straightforward, especially considering your existing implementation.

As for option 2, here's my approach:

  • I opted for this package for my Laravel setup. It's user-friendly to install and configure. There are several packages available for Laravel and other frameworks or languages that serve the same purpose.

  • To implement this in Laravel, I added the following route at the end of my route files (web.php if you're using Laravel) to catch all frontend route requests:

Route::get('/{any}', 'IndexController@index')->where('any', '.*');

In the IndexController, I first verify if the request is from a crawler. If it is, I apply the necessary meta tags. Here's a snippet:

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Butschster\Head\Facades\Meta;
use Butschster\Head\Packages\Entities\OpenGraphPackage;

class IndexController extends Controller
{
    // Code for handling crawlers goes here
}

Subsequently, I created a template solely for the head section - the part of HTML which interests crawlers - to attach and manage the meta tags accordingly:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        @meta_tags

        <link rel="shortcut icon" href="{{ asset('favicon.ico') }}">
    </head>
</html>

Potential Challenges:

  • You must adjust the meta tags depending on the request
  • Maintaining a list of crawlers is essential

Advantages:

  • It's a simple solution that requires minimal alterations to your codebase
  • Efficiently provides lightweight HTML content to the crawler
  • You retain full backend control and with minor tweaks, can establish a sustainable solution

Hoping this sheds light on the subject! Feel free to reach out if there are any ambiguities.

Answer №3

It is essential to incorporate server-side rendering for processing meta tags, as most crawlers do not support JavaScript processing.

Below is an example using PHP - Laravel:

Considering that Vue.js is a Single Page Application (SPA), it renders from a single root page each time.

In Laravel, I configured the route accordingly. Every time, I return the index page with an array of tags and render that page in the view (index page).

  1. Laravel Routing

<?php
    
    use Illuminate\Support\Facades\Route;
    
    Route::get('users/{id}', 'UserController@show');
    
    Route::get('posts/{id}', function () {
        $tags = [
            'og:app_id' => '4549589748545',
            'og:image' => 'image.jpg',
            'og:description' => 'Testing'
        ];
    
        return view('index', compact('tags'));
    });
    
    Route::get('/{any}', function () {
        $tags = [
            'description' => 'Testing',
            'keywords' => 'Testing, Hello world',
        ];
    
        return view('index', compact('tags'));
    })->where('any', '.*');
    
    ?>

  1. Controller

<?php
    
    use App\Http\Controllers\Controller;
    use App\Models\User;
    
    class UserController extends Controller
    {
        public function show(User $user)
        {
            $tags = [
                'og:app_id' => '4549589748545',
                'og:image' => 'image.jpg',
                'og:description' => 'Testing'
            ];
    
            return view('index', compact('tags'));
        }
    }
    
    ?>

  1. Index page

<!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        @foreach($tags as $key => $value)
            <meta property="{{$key}}" content="{{$value}}">
        @endforeach
    </head>
    <body id="app">
    
    <script type="text/javascript" src="{{ asset('js/app.js') }}"></script>
    </body>
    </html>

Answer №4

One option is to set up webpack to automatically include static tags

vue-cli 3 simplifies webpack configuration files by generating them at runtime, so if you need to customize it, you'll have to create a vue.config.js file in the root of your project (which is usually not included by default)

For example:

// vue.config.js
module.exports = {
    configureWebpack: {
        output: {
            publicPath: '/static/'
        },

        plugins: [
          new HtmlWebpackPlugin(),
          new HtmlWebpackTagsPlugin(
            {tags: ['a.js', 'b.css'], append: true },
            {metas: [{
                path: 'asset/path',
                attributes: {
                    name: 'the-meta-name'
                    }}]
            })
        ]
    }
}

(using https://github.com/jharris4/html-webpack-tags-plugin see examples in link for its concrete output)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for adding content to a textarea with JavaScript without disrupting the editing history

I have a specific requirement where I want the user to be able to highlight text in a textarea, then press ctrl + b to automatically surround that selected text with stars. Here is what I envision happening: 1) The initial content of the textarea is: "he ...

Avoid the expansion of line decorations in Monaco editor

I am looking to create a Monaco editor line decoration that remains contained within its original position when I press enter after the decoration. For instance, in a React environment, if I set up a Monaco editor and add a line decoration using the code ...

Delay the closure of a window by utilizing a straightforward method when selecting the "X - CLOSE" option with the following code: `<a href="javascript:window.open('', '_self').close();">X - CLOSE</a>`

I need to implement a delay when users click on the link to close a window. The purpose of this delay is to allow time for playing random "signoff" audio files, such as "Thanks!" or "See you next time!". Currently, without the delay, the audio stops abrupt ...

Using Vue.js to make asynchronous API requests using Axios

I am facing an issue with two versions of code, where the second version is not functioning as expected. I suspect it may be due to a contextual problem that I am unable to pinpoint. The first version of the code works fine: // Fist version (it works) met ...

Setting up a new plugin in ember-cli

Attempting to set up ember-simple-auth in an Ember CLI project, but encountering issues. A new Ember CLI project was created and the following steps were taken to install ember-simple-auth. npm install --save-dev ember-cli-simple-auth ember generate ember ...

Implementing ON DELETE CASCADE using Sequelize

I'm still getting the hang of Sequelize. Currently, I have set up 2 models: Category and User as shown below: const Category = sequelize.define('Category', { id: { type: DataTypes.INTEGER, autoIncrement: true ...

Organizing JSON Objects Within Each Iteration

Greetings! Coming from a PHP background, I am currently exploring the task of grouping JSON data using JavaScript/jQuery. My goal is to group the data by extracting the SKU and then organizing the records together. While I have managed to achieve this, I a ...

Steps for making a CSS animation that horizontally moves an element

I've been working on a React project that features a horizontally scrolling banner. The animation for the automatic scroll is set up like this: "@keyframes marquee": { "0%": { transform: "translateX(0);" }, &quo ...

It's incredibly frustrating when the Facebook like button is nowhere to be found

Currently, I am in the process of developing a website for a local bakery and have decided to incorporate a Facebook like button on the homepage. After visiting developers.facebook, I proceeded to copy and paste the code provided. It appears that there use ...

Retrieving subscriber count from Feedburner using jQuery and JSON

Is there a way to showcase the total number of feedburner subscribers in a standard HTML/jQuery environment without using PHP? The code snippet should be functional within a typical HTML/jQuery page. Perhaps something along these lines: $(document). ...

Executing a python script from an html page by providing it with a string input

I've been searching everywhere for a solution to this problem, but I've only been able to find information on either the python side or the javascript side, and I really need both. Currently, I have a python script that I execute in the command ...

The functionality of Semantic UI Tabs in Angular 4 can only be activated after a page

I've established semantic UI tabs within my component's template.html in the following manner: <div class="ui top attached tabular menu"> <a class="item active" data-tab="global" >Global</a> <a class="item" data-tab= ...

Using jQuery to send a POST request with a data object

Trying to figure out something. Attempting to post an object using jQuery Ajax POST, like so: var dataPostYear = { viewType:GetViewType(), viewDate:'2009/09/08', languageId:GetLanguageId() }; $.ajax({ type: "POST", url: url ...

Encountering issues trying to display state value retrieved from an AJAX call within componentDidMount in React

I recently implemented an AJAX call in my React application using Axios, but I am a bit confused about how to handle the response data. Here is the code snippet that I used: componentDidMount() { axios.get('https://jsonplaceholder.typicode.com/us ...

The Axios API request is made, but fails to retrieve any data back to the client

I've been working on a feature in my VueJS app where I need to restrict page viewing of an Upload instance to only members of a specific Group. However, I'm facing an issue with retrieving the group information. Despite axios successfully hittin ...

Is there a different option than jQuery.ajaxSettings.traditional for use with XMLHttpRequest?

Is there a different option similar to jQuery.ajaxSettings.traditional for xmlhttprequest? or What is the method to serialize query parameters for an xmlhttprequest? ...

Can the meta viewport be eliminated using a script?

I currently do not have access to the HTML file on the server side, but I can make changes by adding additional HTML to the website. My issue is with a non-responsive site listed below, which now requires me to zoom in after it loads due to the new viewpor ...

Switch between player and computer turns in a card game created with react.js

Imagine a hearts card game with 4 players, who play in an anticlockwise direction: p1 -> p2 -> p3 -> p4. If p3 is the first to play, select cards for p3 and p4, then wait for the user to select a card before moving on to p2. If p4 starts, choos ...

Using Django for Underscore and Backbone templating

When I define my JavaScript templates within: <script type="text/template"> </script> they are not rendered in the HTML (I don't see them on the page) in my Django application. Could one of the filters or middlewares I have declared be e ...

Utilizing the arr.push() method to replace an existing value within an array with a new object, rather than simply adding a new

Seeking help to dynamically render a list of components that should expand or shrink based on values being added or removed from an array of custom objects. However, facing an issue where pushing a value into the array only replaces the previous value inst ...