Search code examples
reactjsnext.jsseonext.js13

Add FAQ structured data snippets to Nextjs13


I'm working on a blog using NextJs13 and Strapi and in some pages/posts, I have a FAQ section.

I'm rendering my page SERVER SIDE

I used this solution on my [slug]/page.tsx:

const Place = async ({
  params,
}: {
  params: { slug: string; city: string; lang: string };
}) => {
  const id = params.slug.split('-').slice(-1)[0];
  if (!Number.isFinite(parseInt(id))) {
    notFound();
  }
  const place = await fetchPlace(id);
  const morePlaces = await fetchMorePlaces();

  if (
    params.slug != `${slugify(place.name)}-${place.id}` ||
    params.city != place.city
  ) {
    redirect(
      `/${place.city}/${params.lang}/places/${slugify(place.name)}-${place.id}`
    );
  }

  return (
    <>
      <Script id="faq-schema" strategy="afterInteractive">
        {JSON.stringify({
          "@context": "http://schema.org",
          "@type": "FAQPage",
          "mainEntity": place.faq && place.faq.length > 0 && place.faq.map((item) => ({
            "@type": "Question",
            "name": item.title,
            "acceptedAnswer": {
              "@type": "Answer",
              "text": item.content,
            },
          }))
        })}
      </Script>

      <Carousel place={place} />
      <Layout>
        <div className="lg:pt-40 lg:pb-24 md:py-32 sm:py-24 py-8 lg:px-28 md:px-20">
          <Header place={place} />
          <Content place={place} />
          {place.faq && place.faq.length > 0 && <FAQSection faq={place.faq} />}
        </div>
        <CardsSection places={morePlaces} params={params} />
      </Layout>
      <StickyDiv />
    </>
  );
};

But it seems that this solution is not working and doesn't get detected by google, and it somehow triggers an error on the development server saying : Unhandled Runtime Error - SyntaxError: Unexpected token ':'

How can I add the FAQ structured data snippets to my code and make it work correctly and detected by google. Thank you


Solution

  • Instead of passing the structured data for your script tag as children render them directly as raw markup as seen below. Furthermore, there is no need to use the next/script component for this purpose. A normal script tag will suffice.

    const MyComponent = () => {
      const schema = {
        "@context": "http://schema.org",
        "@type": "FAQPage",
        mainEntity:
          place.faq &&
          place.faq.length > 0 &&
          place.faq.map((item) => ({
            "@type": "Question",
            name: item.title,
            acceptedAnswer: {
              "@type": "Answer",
              text: item.content,
            },
          })),
      };
    
      return (
        <script
          dangerouslySetInnerHTML={{
            __html: JSON.stringify(schema),
          }}
        />
      );
    };