Meta fields are undefined

Hello,
My project is a nuxt 3 SSR website with typescript. Vue files use composition API. I'm trying to fill seo meta with prismic fields data meta_title and meta_description but event if payload is puplated, SEO are still undefined.
Here's my code (example with my homepage and simplified) :

const prismic = usePrismic()
import { useSeo } from '@/composables/useSeo';

const { data: home, error} = useAsyncData(
  'home',
  async () => {
    const response = prismic.client.getSingle<HomepageDocument>('homepage', {lang: 'fr-fr'})
    // some other stuff and queries...
    return {
      data: response.data,
    }
  } 
);

const metaTitle: ComputedRef<string> = computed<string>(() => !isFilled.keyText(home.value?.data.meta_title) ? `${home.value?.data.meta_title}` : `Some default title`);
const metaDescription: ComputedRef<string | undefined> = computed<string |undefined>(() => `${home.value?.data.meta_description}`);

useSeo({
  title: metaTitle.value,
  description: metaDescription.value,
  image: null,
  imageAlt: null
});

useSeo is a composable in typescript :

import type {RuntimeConfig} from "@nuxt/schema";

export interface IItem {
  title: string,
  description?: string,
  image?: any,
  imageAlt?: string | null
}

export const useSeo = (item: IItem): void => {
  const { t, locale } = useI18n();
  const url: URL = useRequestURL()
  const config: RuntimeConfig = useRuntimeConfig();
  const facebookAppId: string = config.public.facebookAppId as string;
  const titleName: string = t('layout.title')

  useSeoMeta({
    fbAppId: facebookAppId,
    title: (): string => item.title,
    description: item.description,
    ogUrl: (): string =>  `${url}`,
    ogType: 'website',
    ogTitle: (): string => `${item.title} | ${titleName}`,
    ogDescription: () =>  item.title,
    ogImage: item.image ?? defaultImg,
    ogImageAlt: (): string =>  item.imageAlt ?? '',
    ogLocale: (): string => locale.value,
    ogSiteName: (): string =>  titleName,
    twitterCard: 'summary_large_image',
    twitterSite: (): string =>  titleName,
    twitterTitle: (): string =>  `${item.title} | ${titleName}`,
    twitterDescription: (): string => (undefined !== item.description) ? item.description : '',
    twitterImage: item.image ?? defaultImg,
    twitterImageAlt: () =>  item.imageAlt
  )
})

Anchor title and description as marked as "undefined" and window social media share are empty. I made sure i'm using SSR and NOT CSR. I've try to replace useAsyncData with useLazyAsyncData or change useSeoMeta by useServerSeoMeta but without result. In Prismic back-office, fields meta* are typed as prismic.KeyTextField.

Hosting provider

Project hosted on netlify.

I've looked at SEO documentation for next in prismic documentation, but nothing for nuxt :( .

Thank you for answers

Hi @balistik.fonfon,

It looks like your useAsyncData and the way you are accessing data may be incorrect.

Could try this version?

const { data, error} = useAsyncData(
  'home',
  async () => {
    const homepage = prismic.client.getSingle<HomepageDocument>('homepage', {lang: 'fr-fr'})
    // some other stuff and queries...
    return {
      homepage,
    }
  } 
);

const metaTitle = computed(() => data.value?.homepage.data.meta_title || "Some default title");
const metaDescription = computed(() => data.value?.homepage.data.meta_description);

useSeo({
  title: metaTitle.value,
  description: metaDescription.value,
  image: null,
  imageAlt: null
});

Here's what I changed:

  • Removed the data: home alias since it causes some confusion.
  • Renamed response to homepage so it's clear what the property contains.
  • Returned homepage rather than homepage.data so you can access the homepage's metadata if needed.
  • Simplified metaTitle and metaDescription.
1 Like

Hello, thank you for answer, I modified my code following your corrections. Meta Title, description are displayed in browser but after page is fully loaded. So this is well populated in the DOM but in facebook share tools, the card is still showing "undefined". Is it possible to fix that ?

Hey @balistik.fonfon,

From the code you shared, this looks like a reactivity issue. This makes the composable use the "first value" you provide it with instead of staying reactive to it.

For example, when you do:

useSeo({
  title: metaTitle.value,
  /* ... */
});

You're passing the current value of metaTitle ("Some default title") to useSeo, not a reactive reference to metaTitle.

I believe refactoring the useSeo composable to accept reactive values would help, so that you can use it with:

useSeo({
  title: metaTitle,
  /* ... */
});

And then:

export interface IItem {
  title: string | Ref<string>,
  /* ... */
}

export const useSeo = (item: IItem): void => {
  useSeoMeta({
    title: (): string => unref(item.title),
    /* ... */
  })

Let us know if that helps :) Happy to dig into it more as needed!

1 Like

All right, i've edited my composable and the use of it like your example :) .
Should i use :

const metaTitle = computed(() => ...

or

const metaTitle: ComputedRef<string> = computed<string>(() =>

?

Or i thinked to an other way, instead of IItem type for useSeo, i can change to my PrismicDocument type, example (simplified):

// template
const { data: stuff, error} = useAsyncData(
    uid,
    async () => await prismic.client.getByUID<StuffTypeDocument>('stuff_type', uid)
)

useSeo(stuff)

// composable
export const useSeo = (item: AllDocumentTypes): void => {
  useSeoMeta({
    title: (): string => item.value.data.meta_title,
    // etc...
  })
}

Hi @balistik.fonfon, you can use the shorter version, in most cases TypeScript will infer the right type for you so there's no need to type it explicitly. And if needed you can just provide a generic on `computed and let TypeScript type the constant as expected.

const metaTitle = computed(() => ...

// As an alternative, as needed
const metaTitle = computed<string>(() => ...

The refactor you suggested should also work yes!

I applied also a solution found here : prismic-demo-flowrise/layouts/default.vue at f10c41835ddfffd9b58ecac62abdae4890979555 · lihbr/prismic-demo-flowrise · GitHub
I set default value in layouts. And it seems it's working :o .

1 Like