0

I'm working on a Nuxt 3 application where I need to dynamically populate meta tags with the data of an article.

Issue: The problem I'm facing is that the meta tags do not get populated with the dynamic data from the article object. If I use static data, the meta tags are populated correctly, but when I try to dynamically set them, they remain empty or fallback to default values.

I've tried the following approaches without success:

Using useHead, useServerHead, etc. Using reactive properties and watchers Ensuring data is fetched before setting meta tags ( not sure )

The component is structured as follows:

<script setup>
import { useArticleStore } from "@/stores/article";
import { ref, watch, onBeforeMount } from "vue";
import { useRouter } from "vue-router";

// Use the article store
const articleStore = useArticleStore();

// Use the router and route
const router = useRouter();
const route = useRoute();

// Define reactive article properties
const article = reactive({
  title: "",
  description: "",
  imgpath: "",
  url: "",
  content: "",
  introduction: "",
  pdfpath: "",
  status: "",
  viewcount: "",
  tags: [],
  type: "",
  author: {},
  authorsArticlesIds: "",
  files: "",
  id: "",
  magazine: {},
});

const loading = ref(true);
const error = ref(null);

// Helper function to get the full URL
const getFullUrl = () => {
  return (
    "website" + window.location.pathname
  );
};

// Fetch the article data
const fetchArticleData = async (id) => {
  if (loading.value) {
    try {
      const fetchedArticle = await articleStore.getArticleById(id);
      article.title = fetchedArticle.article_title;
      article.description = fetchedArticle.article_description;
      article.imgpath = fetchedArticle.article_imgpath.startsWith("http")
        ? fetchedArticle.article_imgpath
        : `website${fetchedArticle.article_imgpath}`;
      article.url = getFullUrl();
      article.content = fetchedArticle.article_htmlcontent;
      article.introduction = fetchedArticle.article_introduction;
      article.pdfpath = fetchedArticle.article_pdfpath;
      article.status = fetchedArticle.article_status;
      article.viewcount = fetchedArticle.article_viewcount;
      article.tags = fetchedArticle.article_tags;
      article.type = fetchedArticle.article_type;
      article.author = fetchedArticle.author;
      article.authorsArticlesIds = fetchedArticle.author.articles;
      article.files = fetchedArticle.files;
      article.id = fetchedArticle.id;
      article.magazine = fetchedArticle.magazine;

      loading.value = false;
    } catch (err) {
      error.value = "Error fetching article data";
      console.error("Error fetching article data:", err);
    }
  }
};
// Set the meta tags after fetching the data
useHead({
  title: article.title || "xyz Magazine",
  meta: [
    { name: "title", content: article.title },
    { name: "description", content: article.description },
    { name: "image", content: article.imgpath },
    { name: "url", content: article.url },

    { property: "og:title", content: article.title },
    { property: "og:description", content: article.description },
    { property: "og:image", content: article.imgpath },
    { property: "og:image:width", content: "256" },
    { property: "og:image:height", content: "256" },
    { property: "og:url", content: article.url },
    { property: "og:locale", content: "ar_AR" },
    { property: "og:type", content: "website" },
    { property: "og:thumbnailUrl", content: article.imgpath },

    // Twitter Card
    { name: "twitter:card", content: "summary_large_image" },
    { name: "twitter:site", content: "@xyzmagazine" },
    { name: "twitter:creator", content: "@xyzmagazine" },
    { name: "twitter:title", content: article.title },
    { name: "twitter:description", content: article.description },
    { name: "twitter:image", content: article.imgpath },
    { name: "twitter:image:alt", content: article.title },
  ],
  link: [{ rel: "canonical", href: article.url }],
});

// Fetch data on the server-side and client-side
onBeforeMount(async () => {
  await fetchArticleData(route.params.id);
});

// Define computed properties
const isPdfPathValid = computed(() => {
  return !article.pdfpath.includes("isShallow=false");
});

// Navigation functions
const navigateToAuthorPage = (authorId) => {
  router.push({ name: "authors-id", params: { id: authorId } });
};

const navigateToArticlesbyTag = (tag) => {
  let tagName = tag.tag_name;
  router.push({ name: "tags-id", query: { tag_name: tagName } });
};

const openArticleWindow = () => {
  window.open(article.pdfpath, "_blank");
};
</script>

Attempted Solutions:

Static Data: Using static data like title: "Static Title" works perfectly. Dynamic Data: Using title: article.title || "Default Title" or title: () => article.title || "Default Title" does not update the meta tags with the dynamic data.

What I Suspect:

I suspect that the issue might be related to the lifecycle of the component and SSR (Server-Side Rendering). The meta tags might be getting set before the data is fully fetched. Additionally, I’m not sure if the reactive properties are causing issues with how the meta tags are being set.

Question:

How can I ensure that the meta tags are populated with the dynamic data from the article object once it is fetched? Is there a recommended approach for handling this in Nuxt 3 with SSR?

Any help or insights into resolving this issue would be greatly appreciated!

0