Code Monkey home page Code Monkey logo

react-schemaorg's Introduction

react-schemaorg npm version Node.js CI Coverage Status

react-schemaorg

Easily insert valid Schema.org JSON-LD in your React apps.

This library provides <JsonLd> for plain React apps, and helmetJsonLdProp() for use with <Helmet>.

Uses schema-dts for Schema.org TypeScript definitions.

Note: This is not an officially supported Google product.

Usage

Install react-schemaorg and your desired version of schema-dts:

npm install schema-dts
npm install react-schemaorg

Plain React Usage

To insert a simple JSON-LD snippet:

import { Person } from "schema-dts";
import { JsonLd } from "react-schemaorg";

export function GraceHopper() {
  return (
    <JsonLd<Person>
      item={{
        "@context": "https://schema.org",
        "@type": "Person",
        name: "Grace Hopper",
        alternateName: "Grace Brewster Murray Hopper",
        alumniOf: {
          "@type": "CollegeOrUniversity",
          name: ["Yale University", "Vassar College"],
        },
        knowsAbout: ["Compilers", "Computer Science"],
      }}
    />
  );
}

Directly creating <script> tags (for next/head and elsewhere)

Certain <head> management libraries require <script> tags to be directly included, rather than wrapped in a component. This includes NextJS's next/head, and react-helmet. With these, we can use the jsonLdScriptProps export to do the same thing:

import { Person } from "schema-dts";
import { jsonLdScriptProps } from "react-schemaorg";
import Head from "next/head";

export default function MyPage() {
  return (
    <Head>
      <script
        {...jsonLdScriptProps<Person>({
          "@context": "https://schema.org",
          "@type": "Person",
          name: "Grace Hopper",
          alternateName: "Grace Brewster Murray Hopper",
          alumniOf: {
            "@type": "CollegeOrUniversity",
            name: ["Yale University", "Vassar College"],
          },
          knowsAbout: ["Compilers", "Computer Science"],
        })}
      />
    </Head>
  );
}

To set JSON-LD in React Helmet, you need to pass it to the script={[...]} prop array in the Helmet component:

import { Person } from "schema-dts";
import { helmetJsonLdProp } from "react-schemaorg";
import { Helmet } from "react-helmet";

<Helmet
  script={[
    helmetJsonLdProp<Person>({
      "@context": "https://schema.org",
      "@type": "Person",
      name: "Grace Hopper",
      alternateName: "Grace Brewster Murray Hopper",
      alumniOf: {
        "@type": "CollegeOrUniversity",
        name: ["Yale University", "Vassar College"],
      },
      knowsAbout: ["Compilers", "Computer Science"],
    }),
  ]}
/>;

Developers

Use NPM to install dependencies:

npm install

Use tsc to build:

tsc

To contribute changes, see the CONTRIBUTING.md file.

react-schemaorg's People

Contributors

bfellows37 avatar dependabot[bot] avatar eyas avatar ntucakovic avatar nyn0x avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-schemaorg's Issues

How to make BreadcrumbList with multiple breadcrumb trails

According to the Google docs here it should be possible to have multiple BreadcrumbList items in an array inside the script tag. However, this doesn't seem possible with react-schemaorg since you cannot pass an array of objects to jsonLdScriptProps() you can only pass a single object.

What I would like to do is:

  const data = jsonLdScriptProps<BreadcrumbList[]>(
[{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "@id": "/#breadcrumlist",
    itemListElement: [
      {
        "@type": "ListItem",
        position: 1,
        name: "Articles",
        item: "https://example.com/",
      },
      {
        "@type": "ListItem",
        position: 2,
        name: "Archive",
        item: `https://example.com/a/${utcYear}/${utcMonth}/`,
      },
    ],
  },
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "@id": "/#breadcrumlist",
    itemListElement: [
      {
        "@type": "ListItem",
        position: 1,
        name: "Home",
        item: "https://example.com/",
      },
      {
        "@type": "ListItem",
        position: 2,
        name: `${section}`,
        item: `https://example.com/sections/${section}/`,
      },
    ],
  },
);

But this generates an error:

Type 'BreadcrumbListLeaf[]' does not satisfy the constraint 'Thing'.
  Property '"@type"' is missing in type 'BreadcrumbListLeaf[]' but required in type 'TouristDestinationLeaf'.ts(2344)

Script not showing up in page source

Hi,

I'm new to this, so maybe I'm missing something... I'm under the impression that the point of this library is to be able to insert JSON-LD in any functional component, and also be readable by Google Rich Results (that apparently will replace GDT).

I have inserted this sample in my functional component, displayed when I'm at localhost:3000/integrations (redirection handled via React Router. This is a single JSON-LD object, but eventually there will be a loop to create multiple ones with variables. The "name" property already has a variable as it's value, as you can see below.

return (
        <div>
            <JsonLd<Person>
                item={{
                    "@context": "https://schema.org",
                    "@type": "Person",
                    "address": {
                        "@type": "PostalAddress",
                        "addressLocality": "Colorado Springs",
                        "addressRegion": "CO",
                        "postalCode": "80840",
                        "streetAddress": "100 Main Street"
                    },
                    "colleague": [
                        "http://www.example.com/JohnColleague.html",
                        "http://www.example.com/JameColleague.html"
                    ],
                    "email": "[email protected]",
                    "image": "janedoe.jpg",
                    "jobTitle": "Research Assistant",
                    "name": name,
                    "alumniOf": "Dartmouth",
                    "birthPlace": "Philadelphia, PA",
                    "birthDate": "1979-10-12",
                    "height": "72 inches",
                    "gender": "female",
                    "memberOf": "Republican Party",
                    "nationality": "Albanian",
                    "telephone": "(123) 456-6789",
                    "url": "http://www.example.com",
                        "sameAs" : [ "https://www.facebook.com/",
                                    "https://www.linkedin.com/",
                                    "http://twitter.com/",
                                    "http://instagram.com/",
                                    "https://plus.google.com/"]
                }}
            />
        </div>
)

However, when I check the page source, I only get the contents of my index.tsx which is the standard React prebuilt code with the root div where the App component goes. My question is this: Where do the inserted scripts go when I use the JSONLd component? Because when I go run them in the validator, they are not found.

Thank you!

Consider not using dangerouslySetInnerHTML

Thank you for this package, I came across it following this issue: #9, as I'm still learning about dangerouslySetInnerHTML and XSS.

I noticed that this package uses dangerouslySetInnerHTML:

      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(this.props.item, safeJsonLdReplacer, this.props.space)
        }}
      />

In order to modify head on the website I'm working on, I use https://github.com/staylor/react-helmet-async which is an improved fork of https://github.com/nfl/react-helmet, for React 16+.

I noticed https://github.com/nfl/react-helmet readme has an example for JSON-LD which doesn't use dangerouslySetInnerHTML:

<Helmet>
    {/* inline script elements */}
    <script type="application/ld+json">{`
        {
            "@context": "http://schema.org"
        }
    `}</script>
</Helmet>

(please note that I added the first and last lines to make the example shorter)

I suppose in the case of https://github.com/google/react-schemaorg, such a syntax (not using dangerouslySetInnerHTML) wasn't followed because it has constraints:

I suppose https://github.com/google/react-schemaorg is aiming for simplicity and not using any Helmet package for this reason.


If within a project we have the choice then what would you recommend please?

Option a)

      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(this.props.item, safeJsonLdReplacer, this.props.space)
        }}

Option b)

    <Helmet>
      <script type="application/ld+json">{JSON.stringify(this.props.item, safeJsonLdReplacer, this.props.space)}</script>
    </Helmet>

Thank you in advance for your time!

Extend JsonLd component to accept stringify's space property

I have quite long JSON-LD schemas, and it would be very useful if we could support passing down space property down to JSON stringify.

Something like:

export class JsonLd<T extends Thing> extends React.Component<{
  item: WithContext<T>;
  space?: string | number;
}> {
  render() {
    return (
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(this.props.item, safeJsonLdReplacer, this.props.space)
        }}
      />
    );
  }
}

I could do a PR with this

Breaking potentialAction in Website type

The schema:

{
    "@context": "http://schema.org",
    "@type": "WebSite",
    "url": "http://example.com/",
    "potentialAction": {
      "@type": "SearchAction",
      "target": "http://example.com/search?&q={query}",
      "query": "required"
    }
}

As specified in https://schema.org/SearchAction doesn't compile. It throws at potentialAction, lots of errors in SearchAction, target, etc. Could you point me to the right direction to fix it? I'd gladly help.

Thanks!

EDIT: Fixed removing target, so I believe the error is from schema-dts. Issue opened there google/schema-dts#45

Return undefined for null values?

image

What do you think about omitting null values, so those fall in the same bucket as the default switch case? I'd prefer to have a clean output with all valid values, and skipping null entries without having to check in each <JsonLd> invocation?

        switch (typeof value) {
            case "object":
                return value; // JSON.stringify will recursively call replacer.
            case "number":
            case "boolean":
            case "bigint":
                return value; // These values are not risky.
            case "string":
                return value.replace(/[&<>'"]/g, replace);
            default: {
                // We shouldn't expect other types.
                isNever(value);
                // JSON.stringify will remove this element.
                return undefined;
            }
        }

Dynamic data

Hello,

Any example of dynamic data? I would like to fill up reviews from an array.

Thanks

Structure is not well defined, how proper use it with schema-dts

Hello, im using "react": "^16.6.0"

The sample repo code is this one (which have a syntax error):

import { Person } from "schema-dts";
import { JsonLd } from "react-schemaorg";

export function GraceHopper() {
  return <JsonLd<Person>
    item={{
      "@context": "https://schema.org",
      "@type": "Person",
      name: "Grace Hopper",
      alternateName: "Grace Brewster Murray Hopper",
      alumniOf: {
        "@type": "CollegeOrUniversity",
        name: ["Yale University", "Vassar College"]
      },
      knowsAbout: ["Compilers", "Computer Science"]
    }}/>;
}

If i use code like this all works fine

import { JsonLd } from "react-schemaorg";

export function personJsonLd() {
  const sampleData = {
    "@context": "https://schema.org",
    "@type": "Person",
    name: "Grace Hopper",
    alternateName: "Grace Brewster Murray Hopper",
    alumniOf: {
      "@type": "CollegeOrUniversity",
      name: ["Yale University", "Vassar College"]
    },
    knowsAbout: ["Compilers", "Computer Science"]
  };

  
  return <JsonLd item={sampleData}/>;
}

But as you can see in the previous code i haven't used the DTS, how can i use the schema-dts library, i didn't figure it out ๐Ÿ˜ž
i have try the return like:

  return (
    <JsonLd item={sampleData}>
      <Person />
    </JsonLd>
  );

But got a bunch of console error, some light/improved example would be much appreciated ๐Ÿ™๐Ÿฝ
Thanks in advance

XSS vulnerability when using SSR

There's a potential XSS problem when using this library with server-side-rendering (which is arguably one of the most prominent use-cases to render json-ld):

const React = require("react");
const express = require("express");
const ReactDOMServer = require("react-dom/server");
const { JsonLd } = require("react-schemaorg");

const dangerous = "</script><script>alert('xss')</script>";

express()
  .get("/", (req, res) =>
    res.send(
      ReactDOMServer.renderToString(
        <div>
          <p>It's ok here: {dangerous}</p>
          <p>
            But not here: <JsonLd item={{ name: dangerous }} />
          </p>
        </div>
      )
    )
  )
  .listen(2000, () => console.log("Listening on port 2000"));

This will result in an alert being shown when accessing http://localhost:2000. Here's a repo to quickly reproduce the issue: https://github.com/DeX3/react-schemaorg-ssr-xss-poc

Option to not include script tags

There are use cases (mine for example) where I don't want the script tags. In fact I would suggest there should not be any script tags, a library user can always add them.

Error when trying to use BreadcrumbList schema.

So setting up the structure for my BreadcrumbList i end up with this here:

export function BreadCrumbSchema() {
  return <JsonLd<BreadcrumbList>
    item={{
      "@context": "https://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": [
       {
        "@type": "ListItem",
        position: 1,
        item:
        {
         "@id": "https://example.com/dresses",
         name: "Dresses"
        }
       },
      ]
     }} />;
}

Yet this produces ts errors when trying to run npm start.

Errormesage

TypeScript error: No overload matches this call.
  Overload 1 of 2, '(props: Readonly<{ item: WithContext<BreadcrumbList>; }>): JsonLd<BreadcrumbList>', gave the following error.
    Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }[]' is not assignable to type 'string | ({ "@type": "FinancialService"; } & ThingBase & { "actionableFeedbackPolicy"?: string | ({ "@type": "CreativeWork"; } & ThingBase & { "about"?: string | ({ "@type": "FinancialService"; } & ... 4 more ... & { ...; }) | ... 797 more ... | undefined; ... 94 more ...; "workTranslation"?: ({ ...; } & ... 1 more ...'.
      Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }[]' is not assignable to type 'Thing[]'.
        Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }' is not assignable to type 'Thing'.
          Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }' is not assignable to type 'HowToTool'.
            Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }' is not assignable to type '{ "@type": "HowToTool"; }'.
              Types of property '"@type"' are incompatible.
                Type 'string' is not assignable to type '"HowToTool"'.
  Overload 2 of 2, '(props: { item: WithContext<BreadcrumbList>; }, context?: any): JsonLd<BreadcrumbList>', gave the following error.
    Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }[]' is not assignable to type 'string | ({ "@type": "FinancialService"; } & ThingBase & { "actionableFeedbackPolicy"?: string | ({ "@type": "CreativeWork"; } & ThingBase & { "about"?: string | ({ "@type": "FinancialService"; } & ... 4 more ... & { ...; }) | ... 797 more ... | undefined; ... 94 more ...; "workTranslation"?: ({ ...; } & ... 1 more ...'.
      Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }[]' is not assignable to type 'Thing[]'.
        Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }' is not assignable to type 'Thing'.
          Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }' is not assignable to type 'HowToTool'.
            Type '{ "@type": string; position: number; item: { "@id": string; name: string; }; }' is not assignable to type '{ "@type": "HowToTool"; }'.
              Types of property '"@type"' are incompatible.
                Type 'string' is not assignable to type '"HowToTool"'.  TS2769

    35 |       "@context": "https://schema.org",
    36 |       "@type": "BreadcrumbList",
  > 37 |       "itemListElement": [
       |       ^
    38 |        {
    39 |         "@type": "ListItem",
    40 |         position: 1,

Any clue what might be causing this? I have been following the guidelines from schema.org and that seems to always error out.

Consider renaming helmetJsonLdProp to a more general name / Support for NextJS

Hi,

I think it would be appropriate to rename the helmetJsonLdProp to something more general as it can be with use with other libraries like next/head for the NextJS Framework. People might not know they can use the function in order to access innerHTML, because of the specifity of the name.

Example Usage with NextJS

<script type={jsonLd.type} dangerouslySetInnerHTML={{
      __html: jsonLd.innerHTML
}}></script>

I would sugget to change the name to maybe scriptJsonLdProp or headJsonLeadProp.

JSON-LD scripts with "@graph" key

I am using Organization and WebSite schema. I can add more than one schema if I add more script tags, but I can use "@graph" too. I have no idea how implement this with schema-dts types. There are some way to do this? Now I'm using "any" type:

<Head>
  <script
    {...jsonLdScriptProps<any>({
      '@context': 'https://schema.org',
      '@graph': [
        {
          '@type': 'Organization',
          name: 'Company LTDA',
          email: '[email protected]',
          telephone: '000-111-222-333'
        },
        {
          '@type': 'WebSite',
          name: 'Company LTDA',
          url: 'https://company.com'
        }
      ]
    })}
  />
</Head>

Schema not showing up in Structured Data Tool

Would there be a reason why, when putting my webpage in the STD (https://search.google.com/structured-data/testing-tool/u/0/) that it would not detect schema on the site when using this method in react? What it actually prints on the page looks fine (see below).

Would this still be fine for SEO even though it does not find it in the structured data tool?

<script type="application/ld+json">
{"@context":"https://schema.org","@type":"FinancialService","legalName":"Sambla AS","name":"Sambla","logo":"https://s3-eu-west-1.amazonaws.com/tpd/logos/55152ede0000ff00057e5303/0x0.png","image":"https://s3-eu-west-1.amazonaws.com/tpd/logos/55152ede0000ff00057e5303/0x0.png","telephone":"33222970","url":"https://www.sambla.no","email":"[email protected]","address":{"@type":"PostalAddress","streetAddress":"Arbins gate 2","addressLocality":"Oslo","postalCode":"0253"},"openingHours":["Mo,Tu,Th 09:00-20:30","We 13:00-20:30","Fr 09:00-17:00"],"priceRange":"10000 - 500000"}
</script>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.