[Vuejs]-Nuxt 3 – How to add Meta tags on a Dynamic route at Build

0👍

I believe you need a server to fix this problem for you so pages should be SSR or you need to generate these pages in build time (SSG).

you can use useHead composable too, but I think you should use SSR or SSG at least for these pages:

The properties of useHead can be dynamic, accepting ref, computed, and reactive properties. meta parameter can also accept a function returning an object to make the entire object reactive.

learn more here: https://nuxt.com/docs/api/composables/use-head

pls check this:https://stackblitz.com/edit/nuxt-starter-51pnfj

and there is a mistake in your code. your code should be this if you using composition API :

<Head>
  <Meta hid="og:url" property="og:url" :content="`https://my-site.com${route.path}`" />
</Head>

<script setup>
const route = useRoute();
</script>

if you using option API your code should be this:

<Head>
  <Meta hid="og:url" property="og:url" :content="`https://my-site.com${$route.path}`" />
</Head>

if you didn’t set ssr to false, by default it will be true or if you don’t want SSR for all pages I think you can use Hybrid rendering. so the result will be this :

enter image description here

0👍

Have you tried define-nuxt-route-middleware? it allows to run your Composables Function at the build time. So your meta should’ve applied properly for SEO. Instead, i use definePageMeta rather than useHead in every page like so :

[some page].vue

<script setup>
definePageMeta({
  order: 1,
  label: "Perusahaan",
  title: "Perusahaan/Klien",
  description:
    "Kami memudahkan administrasi, semua absensi pekerja dapat dengan mudah dilacak riwayatnya serta memantau serta mengatur kehadiran pekerja dengan Geotagging dan pengelompokan area kerja untuk perusahaan atau klien",
  icon: "domain",
  transparent: true,
  image: "/perusahaan/invoice.png",
});
<script/>

And use useHead once,

middleware/meta.js

export default defineNuxtRouteMiddleware(async (to, from) => {
  let data = null,
    url = null,
    params = null;

  if (to.fullPath?.includes("berita") && to?.params?.slug) {
    url = new URL(`${useRuntimeConfig().public?.database}/Articles/get`);
    params = {
      jsonQuery: JSON.stringify({
        slug: to?.params?.slug,
      }),
      limit: 1,
    };
  } else if (to.fullPath?.includes("faq") && to?.params?.id) {
    url = new URL(`${useRuntimeConfig().public?.database}/FAQ/get`);
    params = {
      jsonQuery: JSON.stringify({
        _id: to?.params?.id,
      }),
      limit: 1,
    };
    // console.log(data);
  }

  if (url && params) {
    Object.keys(params).forEach((key) =>
      url.searchParams.append(key, params[key])
    );

    data = await fetch(url, {
      method: "GET",
    });

    data = await data?.json();
  }

  if (data?.success)
    data = data?.result?.[0];

  if (data || (to?.meta?.title && to?.meta?.description)) {
    useHead(
      useNuxtApp().$metaGenerator(
        data?.title || to?.meta?.title,
        data?.description || data?.excerpt || to?.meta?.description,
        to?.fullPath,
        data?.picture || to?.meta?.image,
        to?.meta?.keywords
      )
    );
  }
});

$metaGenerator (plugins/index.js) :

export default defineNuxtPlugin((nuxtApp) => {
  return {
    metaGenerator: (
        title,
        description,
        path,
        image,
        keywords,
        site = "@website"
      ) => {
        const defaultKeywords = [
          "lowongan kerja",
        ];
        if (Array.isArray(keywords)) keywords.concat(defaultKeywords);
        else keywords = defaultKeywords.concat(keywords || "");

        if (!image) {
          image = "/favicon.ico";
        }

        const url =
          `${useRuntimeConfig().hostname}${path}` ||
          useRuntimeConfig().hostname;

        return {
          title,
          meta: [
            {
              name: "description",
              content: description,
            },
            {
              rel: "canonical",
              href: url,
            },
            {
              rel: "amphtml",
              href: url,
            },
            {
              name: "keywords",
              content: keywords,
            },
            // google
            {
              itemprop: "name",
              content: title,
            },
            {
              itemprop: "description",
              content: description,
            },
            {
              itemprop: "image",
              content: image,
            },
            // twitter card
            {
              name: "twitter:card",
              content: "summary_large_image",
            },
            { name: "twitter:site", content: site },
            {
              name: "twitter:title",
              content: title,
            },
            {
              name: "twitter:description",
              content: description,
            },
            {
              name: "twitter:image",
              content: image,
            },
            {
              name: "twitter:image:alt",
              content: title,
            },
            {
              name: "twitter:url",
              content: url,
            },
            // Open Graph
            { property: "og:site_name", content: site },
            { property: "og:type", content: "website" },
            {
              property: "og:title",
              content: title,
            },
            {
              property: "og:description",
              content: description,
            },
            {
              property: "og:image",
              content: image,
            },
            {
              property: "og:url",
              content: url,
            },
            {
              property: "og:image:secure_url",
              content: image,
            },
            {
              property: "og:image:alt",
              content: title,
            },
          ],
          link: [
            {
              rel: "canonical",
              href: url,
            },
            {
              rel: "amphtml",
              href: url,
            },
          ],
        };
      },
  }
})

0👍

useSeoMeta works for me while useHead does not. I was trying to change the description meta using the following code.

useSeoMeta({
  description: () => 'New meta description',
});

Leave a comment