Add ld+json script tag in client-side React
Asked Answered
C

6

32

I have currently build a React app. Since it's a SPA, it has a single index.html file. I want to add 2 "ld+json" script tags, i.e for reviews and bookmarks for a certain route.

I've injected the script tag in componentDidMount of that component but the Google Structured Data Testing Tool doesn't read that.

Is it because Google reads directly from index.html and since my script tags are bundled inside main.js, it cannot read it?

Is it possible to do this in client side React? Is server side rendering the only possible way to do it?

-- Detailed Explanation--- I currently want to implement a system like IMDB has i.e whenever we search for a movie in goole; the IMDB search result will show the rating of the movie in the google pages itself. To do that I've need to put a script in my index.html file

<script type='application/ld+json'>
  {
      "@context": "http://schema.org/",
      "@type": "Review",
      "itemReviewed": {
        "@type": "Thing",
        "name": "Name"
      },
      "reviewRating": {
        "@type": "Rating",
        "ratingValue": "3",
        "bestRating": "5"
      },
      "publisher": {
        "@type": "Organization",
        "name": "1234"
      }
    }
</script>

Since my app is an SPA, I cannot put this in my main index.html file.

My current approach: Suppose "/movies/inception" route renders "MovieDetail" component. So, I'm currently adding the script at the end of this component.

import React from 'react';
import JsonLd from '../path_to_JSONLD';

class MovieDetail extends React.Component {
 render(){
 let data = {
  "@context": "http://schema.org/",
  "@type": "Review",
  "itemReviewed": {
    "@type": "Thing",
    "name": "Name"
   },
    "reviewRating": {
     "@type": "Rating",
     "ratingValue": "3",
     "bestRating": "5"
    },
   "publisher": {
     "@type": "Organization",
     "name": "1234"
    }
  }
   return(
    <SOME COMPOENTS />
    <JsonLd data={data} />

 )
}

My JsonLd component

import React from 'react';

const JsonLd = ({ data }) =>
  <script
    type="application/ld+json"
    dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
  />;

  export default JsonLd;

So, when i inspect the component; i can see the dynamically added script tag. But, in the structure testing tool "https://search.google.com/structured-data/testing-tool" . It doesnt show the schema after validation. Hence, I asked whether it can be done via client side or SSR is only solution for this where i can give an updated index.html as a response.

I hope this clears the confusion. Thanks!

Creeper answered 29/5, 2018 at 13:21 Comment(4)
Your question is unclear at least to me and also lacking code. Please read stackoverflow.com/help/how-to-ask and then come back, edit your question/post a new one and we'll try to help.Eh
I am pretty sure it depends on how exactly you are injecting the tags into your page. Without knowing that we would be guessing. It would also be intersting to know if they work when you add them directly to the index.htmlBrit
Updated the code. I hope this clears the confusion. Thanks! @Tim SeguineCreeper
I am no expert on the topic unfortunately, but it seems like other people inject them dynamically into the DOM also, so hopefully someone with more experience here can help.Brit
C
8

Solution: Used "react-meta-tags" Link : https://github.com/s-yadav/react-meta-tags

import React from 'react';
import MetaTags from 'react-meta-tags';
import JsonLd from 'path_to_jsonld';


export default class MetaComponent extends React.Component {
  render() {
   return (
    <div className="wrapper">
      <MetaTags>
        <title>{this.props.title}</title>
        <meta property="og:type" content="website" />
        <meta name="description" content={this.props.description} />
        <meta name="og:description" content={this.props.description} />
        <meta property="og:title" content={this.props.title} />
        <meta property="og:url" content={window.location.href} />
        <meta property="og:site_name" content={"content"} 
         />
        {
          this.props.jsonLd && 
            <JsonLd data={this.props.jsonLd} />
        }
      </MetaTags>
    </div>
  )
 }
}

And then I imported this component in my main component

import React from 'react';
import MetaComponent from '../path_to_Metacomponent';

class MovieDetail extends React.Component {
 render(){
let data = {
  "@context": "http://schema.org/",
  "@type": "Review",
  "itemReviewed": {
    "@type": "Thing",
    "name": "Name"
    },
"reviewRating": {
    "@type": "Rating",
    "ratingValue": "3",
    "bestRating": "5"
   },
 "publisher": {
   "@type": "Organization",
   "name": "1234"
  }
}
   return(
    <SOME COMPOENTS />
    <MetaComponent jsonLd={data} title={"abcd"} description={"xyza"} />

 )
}

What the package does is insert the script tag inside the head tag dynamically and since the script now is not bundled inside the main.js file google is able to read it from the source.

Creeper answered 1/6, 2018 at 13:22 Comment(2)
i understand but little bit incomplete, what is in path_to_Metacomponentare you returning script tag there ?Zelma
The first code snippet i.e I made a component named MetaComponent, so that it is reusable and imported it in MovieDetail Component (the second code snippet). ./path_to_Metacomponent just signifies the path to the MetaComponent. Thats itCreeper
G
31

For me, React Helmet works well.

<Helmet>
    <script className='structured-data-list' type="application/ld+json">{structuredJSON}</script>
</Helmet> 

Where structuredJSON is something like result of such function:

export const structuredDataSingle = (prod, imgPath, availability) => {

    let data = {
        "@context": "http://schema.org/",
        "@type": "Product",
        "name": `${prod.title}`,
        "image": prod.images.map((item) => imgPath + item),
        "description": prod['description'],
        "url": location.href,
        "offers": {
            "@type": "Offer",
            "priceCurrency": `${prod['currency'] || "₴"}`,
            "price": prod['price'] ? `${parseFloat(prod['price'])}` : 0,
            "availability": `${availability}`,
            "seller": {
                "@type": "Organization",
                "name": "TopMotoPro"
            }
        }
    };

    // brand
    if(prod['brand']) {
        data['mpn'] = prod['brand'];
        data['brand'] = {
            "@type": "Thing",
            "name": `${prod['brand']}`
        };
    }

    // logo
    if(prod['logo']){
        data['logo'] = imgPath + prod['logo'];
    }

    return JSON.stringify(data);
};
Gatepost answered 8/11, 2018 at 22:42 Comment(3)
Maybe this is a stupid question, but where does one put this structuredDataSingle object and where are you putting it with <Helmet>? <Helmet> element goes below 'return' in a typical React page? Why does structuredDataSingle have 'if' statements? I thought structured data was just static data of content for SEO.Consignment
1) I have MyHelmet as wrapper component. It gets structuredDataSingle as prop. If the prop passed - I render <script type="application/ld+json">{structuredJSON}</script> inside Helmet; 2) If statements added because on different pages some optional chema org props can be ommited.Gatepost
it's not working with me. Not show on localhost or productionCapon
J
12

You can simply render it dangerously

<script type='application/ld+json' dangerouslySetInnerHTML={ { __html: `{ "@context": "http://schema.org", "@type": "LocalBusiness", ... }`}} />
Jamarjamb answered 11/11, 2019 at 12:19 Comment(0)
C
8

Solution: Used "react-meta-tags" Link : https://github.com/s-yadav/react-meta-tags

import React from 'react';
import MetaTags from 'react-meta-tags';
import JsonLd from 'path_to_jsonld';


export default class MetaComponent extends React.Component {
  render() {
   return (
    <div className="wrapper">
      <MetaTags>
        <title>{this.props.title}</title>
        <meta property="og:type" content="website" />
        <meta name="description" content={this.props.description} />
        <meta name="og:description" content={this.props.description} />
        <meta property="og:title" content={this.props.title} />
        <meta property="og:url" content={window.location.href} />
        <meta property="og:site_name" content={"content"} 
         />
        {
          this.props.jsonLd && 
            <JsonLd data={this.props.jsonLd} />
        }
      </MetaTags>
    </div>
  )
 }
}

And then I imported this component in my main component

import React from 'react';
import MetaComponent from '../path_to_Metacomponent';

class MovieDetail extends React.Component {
 render(){
let data = {
  "@context": "http://schema.org/",
  "@type": "Review",
  "itemReviewed": {
    "@type": "Thing",
    "name": "Name"
    },
"reviewRating": {
    "@type": "Rating",
    "ratingValue": "3",
    "bestRating": "5"
   },
 "publisher": {
   "@type": "Organization",
   "name": "1234"
  }
}
   return(
    <SOME COMPOENTS />
    <MetaComponent jsonLd={data} title={"abcd"} description={"xyza"} />

 )
}

What the package does is insert the script tag inside the head tag dynamically and since the script now is not bundled inside the main.js file google is able to read it from the source.

Creeper answered 1/6, 2018 at 13:22 Comment(2)
i understand but little bit incomplete, what is in path_to_Metacomponentare you returning script tag there ?Zelma
The first code snippet i.e I made a component named MetaComponent, so that it is reusable and imported it in MovieDetail Component (the second code snippet). ./path_to_Metacomponent just signifies the path to the MetaComponent. Thats itCreeper
R
5

As @Дмитрий Дорогонов suggested, you can use React Helmet to inline script elements. You can also include them inline and use variable interpolation as well:

<script type="application/ld+json">{`
  {
    "@context": "http://schema.org",
    "@type": "${typeVariable}"
  }
`}</script>
Roose answered 23/9, 2019 at 14:56 Comment(0)
P
2

This helped me:

  1. Prepare your JSON and stringify it.
const ORG_SCHEMA = JSON.stringify({
  "@context": "http://schema.org",
  "@type": "Organization",
  "name": "Allround",
  "description": "Allround - An online learning experience through creative and co-curricular pursuits for kids aged 5-15. Learn western vocals, keyboard, chess & Spanish from experts.",
  "url": "https://allround.club/",
  "logo": "https://allround.club/favicon.svg",
  "address": {
      "@type": "PostalAddress",
      "streetAddress": "DD3, Diamond District, HAL Old Airport Road",
      "addressLocality": "Bengaluru",
      "addressRegion": "Karnataka",
      "postalCode": "560008",
      "Telephone": "+918035003600"
  },
  "sameAs": [
      "https://www.facebook.com/Allround-Learning",
      "https://www.linkedin.com/company/allround-club",
      "https://www.instagram.com/allround.club/"
  ]
});
  1. Use dangerouslySetInnerHTML to set the stringified JSON in the script tag.
<script type='application/ld+json' dangerouslySetInnerHTML={ { __html: ORG_SCHEMA} } />

You can see how it shows up to crawlers here: https://allround.club/

Plot answered 27/7, 2021 at 9:18 Comment(0)
P
2

You can use react-schemaorg by Google:

https://github.com/google/react-schemaorg

Pettis answered 29/7, 2021 at 20:0 Comment(1)
I recommend this one. It works very well in my projects. Maybe the answer could be enriched.Arrears

© 2022 - 2024 — McMap. All rights reserved.