~/blog/structured-data|
Scan this vCard to save my contacts
blog post cover
#seo

The smartest way to use structured data in React apps

August 20, 2022 · 6 min read

A very quick introduction to JSON-LD

JSON-LD stands for JSON Linked Data, and it's one of the available formats for describing web pages using structured Data. Others are microdata and RDFa.

Linked Data empowers people that publish and use information on the Web. It is a way to create a network of standards-based, machine-readable data across Web sites.

https://json-ld.org/

Structured data is widely used by Google Search to collect information while crawling web pages. Alongside semantically correct HTML structure, structured data is an easy and the most profitable thing we can do as developers to improve the SEO.

Even though it's just a JSON, It's very powerful and helps to describe lots of things from articles and their authors to online events, food recipes, and charity organizations.

One of the examples I borrowed from the Google guidelines:

<script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "SoftwareApplication",
    "name": "Angry Birds",
    "operatingSystem": "ANDROID",
    "applicationCategory": "GameApplication",
    "aggregateRating": {
      "@type": "AggregateRating",
      "ratingValue": "4.6",
      "ratingCount": "8864"
    },
    "offers": {
      "@type": "Offer",
      "price": "1.00",
      "priceCurrency": "USD"
    }
  }
</script>

This piece of JSON placed within <script type="application/ld+json" /> tag, describes a software application to have a better representation of it in the list of search results.

And this is what it looks like on a mobile device at the moment of writing:

SoftwareApplication Example

How to use it in React apps

I hope so far everything is clear, so let's think about how to integrate this structured data into our React app properly.

It's possible to use it as it is, serializing JSON and placing scripts into <head /> section, as follows:

// ⛔️ Not type-safe
<script>
  {JSON.stringify({
    '@context': 'https://schema.org',
    '@type': 'SoftwareApplication',
    title: 'Angry Birds',
  })}
</script>

But is it name or title? Without comparing with the https://schema.org definitions, I can't say for sure.

We can use the power of TypeScript types to protect ourselves from typos and human mistakes. We need 2 packages for this purpose:

yarn add schema-dts react-schemaorg

react-schemaorg supports both <Helmet /> and other <head /> management libraries, like next/head from Next.js. For instance, I assume we need to use it with Helmet:

import { SoftwareApplication } from 'schema-dts';
import { helmetJsonLdProp } from 'react-schemaorg';
import { Helmet } from 'react-helmet';

// ✅ Type safe
<Helmet
  script={[
    helmetJsonLdProp<SoftwareApplication>({
      '@context': 'https://schema.org',
      '@type': 'Software',
      title: 'Angry Birds', // TS2345: Argument of type '{ '@context': "https://schema.org"; '@type': "SoftwareApplication"; title: string; }' is not assignable to parameter of type 'WithContext<SoftwareApplication>'.
    }),
  ]}
/>;

Ohh, now it's clear that title is not the right one:

<Helmet
  script={[
    helmetJsonLdProp<SoftwareApplication>({
      '@context': 'https://schema.org',
      '@type': 'Software',
      name: 'Angry Birds', // 👍
    }),
  ]}
/>

In addition, we got a handy documentation of all the properties and their types:

// node_modules/schema-dts/dist/schema.d.ts

interface SoftwareApplicationBase extends CreativeWorkBase {
  /** Type of software application, e.g. 'Game, Multimedia'. */
  applicationCategory?: SchemaValue<Text | URL, 'applicationCategory'>;

  /** Subcategory of the application, e.g. 'Arcade Game'. */
  applicationSubCategory?: SchemaValue<Text | URL, 'applicationSubCategory'>;

  /** The name of the application suite to which the application belongs (e.g. Excel belongs to Office). */
  applicationSuite?: SchemaValue<Text, 'applicationSuite'>;

  /** Device required to run the application. Used in cases where a specific make/model is required to run the application. */
  availableOnDevice?: SchemaValue<Text, 'availableOnDevice'>;

  // ...
}

As a result, we have:

  • ✅ Type safety
  • ✅ Schema documentation
  • ✅ Autocomplete in IDE
  • ✅ Satisfied colleagues

It's time to deploy and test if everything works fine.