React SEO

Full Stack Developed by: Donald Boulton Follow Don on Twitter🙌 0
|
Jul 30, 20194 min readReact, Json-LD  Category Tech

JSON-LD Per Template

📡 react SEO JSON-LD and meta tags, "per page", is the only logical option for SEO = microdata perfection in a WebPage or Article.

The main issue with Json-LD for microdata is you need frontmatter data from pages and posts like the page title, url, description, created time edited time, author, publisher, ratings, reviews etc.

Doing this with a SEO Component like most react Tutorials means you would have to develop a query to pull all file markdown edges into any page you wanted with Json-LD for all pages and posts frontmatter data along with the body of the page.

I just added Json-LD and individual page meta tags per pages templates.

Below is a example of blog post page using Json-LD with react-helmet for the meta tags.

import React from "react"
import Helmet from "react-helmet"
import RehypeReact from "rehype-react"
import "prismjs/themes/prism-okaidia.css"
import "prismjs/plugins/toolbar/prism-toolbar.css"
import PropTypes from "prop-types"
import { graphql } from "react"
import { HTMLContent } from "../components/Content"
import ArticleTemplate from "../components/ArticleTemplate"
import Share from "../components/Share"
import Comments from "../components/Comments"
import Global from "../components/Global"
import PostCover from "../components/PostCover"
import config from "../../data/config"

const renderAst = new RehypeReact({
  createElement: React.createElement,
  components: {
    "interactive-counter": Counter,
    "interactive-hit-counter": HitCounter,
    "interactive-todo": Todo,
  },
}).Compiler

const ArticlePage = ({ data }) => {
  const { markdownRemark: post } = data

  const postNode = data.markdownRemark
  const buildTime = node.frontmatter.date
  const postImage = node.frontmatter.cover
  const imageWidth = postImage.width
  const imageHeight = postImage.height
  const body = post.html
  const title = node.frontmatter.title
  const readingTime = data.readingTime

  let alternativeHeadline = node.frontmatter.meta_title
  let pageDescription = node.frontmatter.meta_description
  let pageTags = node.frontmatter.tags
  let url = node.frontmatter.slug
  let logo = config.siteLogo

  const articleSchemaOrgJSONLD = {
    "@context": "http://schema.org",
    "@type": "Article",
    publisher: {
      "@type": "Organization",
      name: title,
      logo: {
        "@type": "ImageObject",
        url: logo,
      },
    },
    url: url,
    mainEntityOfPage: {
      "@type": "WebPage",
      "@id": url,
    },
    alternateName: config.siteTitleAlt ? config.siteTitleAlt : "",
    name: title,
    author: {
      "@type": "Person",
      name: "donboulton",
    },
    copyrightHolder: {
      "@type": "Person",
      name: "donaldboulton",
    },
    copyrightYear: config.copyrightYear,
    creator: {
      "@type": "Person",
      name: "donboulton",
    },
    alternativeHeadline: alternativeHeadline,
    datePublished: buildTime,
    dateModified: buildTime,
    description: pageDescription,
    headline: title,
    keywords: pageTags,
    inLanguage: "en_US",
    image: {
      "@type": "ImageObject",
      url: postImage,
    },
    articleBody: body,
    aggregateRating: {
      "@type": "AggregateRating",
      ratingValue: 4.5,
      ratingCount: data.ratingCount,
    },
  }

  return (
    <Global pageTitle={node.frontmatter.title}>
      <Helmet>
        <title>{`${node.frontmatter.title} | ${config.siteTitle}`}</title>
        <meta name="description" content={node.frontmatter.meta_description} />
        <meta name="keywords" content={pageTags} />
        <meta name="url" content={node.frontmatter.slug} />
        <meta property="og:type" content="article" />
        <meta property="og:readingTime" content={readingTime} />
        <meta property="og:title" content={node.frontmatter.title} />
        <meta
          property="og:description"
          content={node.frontmatter.meta_description}
        />
        <meta property="og:image" content={node.frontmatter.cover} />
        <meta property="og:image:alt" content={node.frontmatter.meta_title} />
        <meta property="og:image:width" content={imageWidth} />
        <meta property="og:image:height" content={imageHeight} />
        <meta property="og:url" content={node.frontmatter.slug} />
        <meta name="rel" content={node.frontmatter.slug} />
        <meta name="key" content={node.frontmatter.slug} />
        <meta name="twitter:author" content="donboulton" />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content={node.frontmatter.title} />
        <meta name="twitter:image" content={node.frontmatter.cover} />
        <meta
          name="twitter:description"
          content={node.frontmatter.meta_description}
        />
        <meta name="twitter:widgets:autoload" content="off" />
        <meta name="twitter:widgets:theme" content="dark" />
        <meta name="twitter:widgets:link-color" content="#d64000" />
        <meta name="twitter:widgets:border-color" content="#000000" />
        <meta name="twitter:dnt" content="on" />
        <link rel="canonical" href={node.frontmatter.slug} />
        <link rel="image_src" href={`${config.siteUrl}${logo}`} />
        <link rel="me" href="https://twitter.com/donboulton" />
        {/* Schema.org tags */}
        <script type="application/ld+json">
          {JSON.stringify(articleSchemaOrgJSONLD)}
        </script>
      </Helmet>
      <section className="hero">
        <PostCover postNode={postNode} coverClassName="post-cover" />
      </section>
      <section className="section">
        <div className="container content">
          <div className="columns">
            <div className="column is-10 is-offset-1">
              <ArticleTemplate
                content={post.html}
                contentComponent={HTMLContent}
                cover={node.frontmatter.cover}
                readingTime={readingTime}
                category={node.frontmatter.category}
                date={node.frontmatter.date}
                tweet_id={node.frontmatter.tweet_id}
                meta_title={node.frontmatter.meta_title}
                description={node.frontmatter.meta_description}
                tags={node.frontmatter.tags}
                title={node.frontmatter.title}
              />
              <Share
                title={node.frontmatter.title}
                slug={post.fields.slug}
                excerpt={node.frontmatter.meta_description}
              />
              <hr />
              <Comments />
            </div>
          </div>
        </div>
      </section>
    </Global>
  )
}

ArticlePage.propTypes = {
  data: PropTypes.shape({
    markdownRemark: PropTypes.object,
  }),
  readingTime: PropTypes.number.isRequired,
}

export default ArticlePage

export const pageQuery = graphql`
  query ArticleByID($id: String!) {
    markdownRemark(id: { eq: $id }) {
      id
      htmlAst
      timeToRead
      tableOfContents
      excerpt(pruneLength: 200)
      fields {
        slug
      }
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        title
        tweet_id
        category
        meta_title
        meta_description
        tags
        cover
        canonical
      }
    }
    allRatingsJson(
      filter: { postPath: { eq: $id } }
      sort: { fields: [date], order: ASC }
    ) {
      totalCount
      edges {
        node {
          id
          rating
        }
      }
    }
  }
`

Using the above article query for individual Blog Post pages frontmatter data.



Sooner Girls

National Champions For the Fifth Time, Go Sooners!

Css Tricks

Front-End UI, Javascript & Functions, HTML Tips, and More!

Fox News

The Truth In the Latest World and National News.