31

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!

Ashish Shetty
  • 456
  • 1
  • 4
  • 8
  • Your question is unclear at least to me and also lacking code. Please read https://stackoverflow.com/help/how-to-ask and then come back, edit your question/post a new one and we'll try to help. – Ionut Necula May 29 '18 at 13:29
  • 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.html – Tim Seguine May 29 '18 at 13:32
  • 1
    Updated the code. I hope this clears the confusion. Thanks! @Tim Seguine – Ashish Shetty May 29 '18 at 13:55
  • 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. – Tim Seguine May 29 '18 at 14:13

6 Answers6

30

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);
};
brooksrelyt
  • 3,925
  • 5
  • 31
  • 54
Dima Dorogonov
  • 2,297
  • 1
  • 20
  • 23
  • Maybe this is a stupid question, but where does one put this structuredDataSingle object and where are you putting it with ? 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. – Ant Dec 29 '19 at 01:21
  • 1) I have MyHelmet as wrapper component. It gets structuredDataSingle as prop. If the prop passed - I render `` inside `Helmet`; 2) `If` statements added because on different pages some optional chema org props can be ommited. – Dima Dorogonov Dec 30 '19 at 10:26
  • it's not working with me. Not show on localhost or production – huykon225 Jan 18 '21 at 01:52
12

You can simply render it dangerously

<script type='application/ld+json' dangerouslySetInnerHTML={ { __html: `{ "@context": "http://schema.org", "@type": "LocalBusiness", ... }`}} />
Shivam
  • 3,091
  • 4
  • 30
  • 45
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.

Ashish Shetty
  • 456
  • 1
  • 4
  • 8
  • i understand but little bit incomplete, what is in path_to_Metacomponentare you returning script tag there ? – Azad Hussain Aug 07 '20 at 08:05
  • 1
    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 it – Ashish Shetty Aug 07 '20 at 08:09
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>
Giovanni Benussi
  • 3,102
  • 2
  • 28
  • 30
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/

Ankit Shubham
  • 2,989
  • 2
  • 36
  • 61
2

You can use react-schemaorg by Google:

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

Diego Carrion
  • 513
  • 5
  • 8