6
votes

On a Gatsby site I have an SEO component seo.js using React Helmet that is called inside a blog template blog.js. However Facebook Share does not recognize the opengraph tags. The site is hosted on Gatsby Cloud, not Netlify.

I've searched the webs for answers but can't find anything. This post did not pan out. It does not appear to be caching and re-scraping does nothing.

Tags appear of course when viewing page source directly. At a total loss here. Other sites with similar setups work fine. ¯\_(ツ)_/¯

If anyone has had similar issues, I'd love to hear how they fixed this. Thoughts? Thanks.

Code and error examples below:

# seo.js

import React from 'react'
import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { useStaticQuery, graphql } from 'gatsby'

function SEO({ description, lang, meta, title, pageUrl, image, post, children }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
            siteUrl
            author
          }
        }
      }
    `
  )

  const metaDescription = description || site.siteMetadata.description
  const metaTitle = title || site.siteMetadata.title
  const metaUrl = pageUrl || site.siteMetadata.siteUrl

  return (
    <Helmet
      defer={false}
      htmlAttributes={{
        lang,
      }}
      defaultTitle={metaTitle}
      titleTemplate={`%s`}
      meta={meta}
    >
      {/* General tags */}
      <title>{metaTitle}</title>
      <meta name="image" content={image} />
      <meta name="description" content={metaDescription} />

      {/* OpenGraph tags */}
      <meta property="og:title" content={metaTitle} />
      <meta property="og:type" content={post ? `article` : `website`} />
      <meta property="og:url" content={metaUrl} />
      <meta property="og:image" content={image} />
      <meta property="og:description" content={metaDescription} />

      {/* Twitter Card tags */}
      <meta name="twitter:card" content="summary" />
      <meta name="twitter:creator" content="{site.siteMetadata.author}" />
      <meta name="twitter:title" content={metaTitle} />
      <meta name="twitter:image" content={image} />
      <meta name="twitter:description" content={metaDescription} />

      {children}
    </Helmet>
  )
}

SEO.defaultProps = {
  lang: `en`,
  meta: [],
  description: ``,
}

SEO.propTypes = {
  description: PropTypes.string,
  lang: PropTypes.string,
  title: PropTypes.string,
}

export default SEO
# blogs.js

import React from 'react'
import { graphql, Link } from 'gatsby'
import { Container, Row, Col, useScreenClass, Visible } from 'react-grid-system'

import SEO from '../components/seo'
import Layout from '../components/layout'
import Main from '../components/main'
import BlogList from '../components/insights/blogList'
import Pagination from '../components/pagination'

const Blog = (props) => {
  const {
    seoTitle,
    seoDescription,
    seoHelmet,
  } = props.data.contentfulPage
  const posts = props.data.allContentfulInsightsPage.edges
  const { currentPage } = props.pageContext
  const screenClass = useScreenClass()

  return (
    <Layout>
      <SEO
        title={`${seoTitle} ${currentPage > 1 ?  `- Page ${currentPage}` : `` }`}
        description={seoDescription?.seoDescription ? seoDescription.seoDescription : false}
      >
        {seoHelmet ? seoHelmet.seoHelmet : ''}
      </SEO>
      <Main>
        <h1 className={`headline`}>Blog</h1>
        <BlogList posts={posts} />
        <Pagination context={props.pageContext} />
      </Main>
    </Layout>
  )
}

export const query = graphql`
  query($skip: Int!, $limit: Int!) {
    contentfulPage(slug: {eq: "blog"}) {
      id
      slug
      seoTitle
      seoDescription {
        seoDescription
      }
      seoHelmet {
        seoHelmet
      }
    }
    allContentfulBlogs(
      sort: { fields: [date], order: DESC }
      limit: $limit
      skip: $skip
    ) {
      edges {
        node {
          id
          title
          slug
          date
          preview
          text {
            json
          }
          image {
            localFile {
              ...blogThumbnail
            }
          }
          category {
            name
            slug
          }
        }
      }
    }
  }
`

export default Blog

The error on FB Sharing Debugger:

⚠️ Warnings That Should Be Fixed

Inferred Property

The 'og:image' property should be explicitly provided, even if a value can be inferred from other tags.

Missing Properties

The following required properties are missing: og:url, og:type, og:title, og:image, og:description, fb:app_id

Facebook Sharing Debugger scraped URL result:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style data-href="/styles.13d4056a8a600210f038.css">a,abbr, ... </style>
<meta name="generator" content="Gatsby 2.19.49">
<title data-react-helmet="true"></title>
<link as="font" href=" ... " rel="preload" crossorigin="anonymous">
<style id="jss-server-side"></style>
<style data-styled="" data-styled-version="4.4.1"></style>
<script>window.dataLayer = window.dataLayer || []; ... </script>
<link rel="sitemap" type="application/xml" href="/sitemap.xml">
<link rel="canonical" href=" ... " data-baseprotocol="https:" data-basehost=" ... ">
<link rel="icon" href="/icons/icon-48x48.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#3FB6E8">
<link rel="apple-touch-icon" sizes="48x48" href="/icons/icon-48x48.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="96x96" href="/icons/icon-96x96.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="192x192" href="/icons/icon-192x192.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="256x256" href="/icons/icon-256x256.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="384x384" href="/icons/icon-384x384.png?v=4b382095dbd38c63f79bce6052737f30">
<link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png?v=4b382095dbd38c63f79bce6052737f30">
<link as="script" rel="preload" href="/component---src-templates-insight-js-0b3261c291dd76b6e32d.js">
<link as="script" rel="preload" href="/commons-0606cae2f0bba83915df.js">
<link as="script" rel="preload" href="/app-595ec14f594e84b72fc9.js">
<link as="script" rel="preload" href="/styles-9336dff5557f552f77fc.js">
<link as="script" rel="preload" href="/webpack-runtime-5af2f2a5f9a736a820a9.js">
<link as="fetch" rel="preload" href="/page-data/insights/key-strategies-for-building-websites-that-capture-and-convert/page-data.json" crossorigin="anonymous">
<link as="fetch" rel="preload" href="/page-data/app-data.json" crossorigin="anonymous">
</head>
<body>
<noscript><iframe src="https://www.googletagmanager.com/ns.html? ... " height="0" width="0" style="display: none; visibility: hidden"></iframe></noscript>
<div id="___gatsby">
<div style="outline:none" tabindex="-1" id="gatsby-focus-wrapper"></div>
<div id="gatsby-announcer" style="position:absolute;top:0;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border:0" aria-live="assertive" aria-atomic="true"></div>
</div>
<script id="gatsby-script-loader">/*<![CDATA[*/ ... /*]]>*/</script>
<script id="gatsby-chunk-mapping">/*<![CDATA[*/ ... /*]]>*/</script>
<script src="/webpack-runtime-5af2f2a5f9a736a820a9.js" async=""></script>
<script src="/styles-9336dff5557f552f77fc.js" async=""></script>
<script src="/app-595ec14f594e84b72fc9.js" async=""></script>
<script src="/commons-0606cae2f0bba83915df.js" async=""></script>
<script src="/component---src-templates-insight-js-0b3261c291dd76b6e32d.js" async=""></script>
</body>
</html>

Edit Here are some other fixes I tried...

Edit 2 from my package.json

"dependencies": {
  "@contentful/rich-text-react-renderer": "^13.4.0",
  "@fullpage/react-fullpage": "^0.1.17",
  "@material-ui/core": "^4.9.3",
  "@material-ui/styles": "^4.9.0",
  "@vimeo/player": "^2.10.0",
  "babel-eslint": "^10.0.3",
  "babel-plugin-styled-components": "^1.10.6",
  "eslint": "^6.8.0",
  "eslint-plugin-graphql": "^3.1.1",
  "gatsby": "^2.18.12",
  "gatsby-background-image": "^0.9.12",
  "gatsby-cli": "^2.9.0",
  "gatsby-image": "^2.2.34",
  "gatsby-plugin-canonical-urls": "^2.1.20",
  "gatsby-plugin-google-tagmanager": "^2.1.24",
  "gatsby-plugin-manifest": "^2.2.31",
  "gatsby-plugin-material-ui": "^2.1.6",
  "gatsby-plugin-offline": "^3.1.1",
  "gatsby-plugin-preload-fonts": "^1.0.34",
  "gatsby-plugin-react-helmet": "^3.2.1",
  "gatsby-plugin-robots-txt": "^1.5.0",
  "gatsby-plugin-sass": "^2.1.26",
  "gatsby-plugin-sharp": "^2.3.5",
  "gatsby-plugin-sitemap": "^2.2.26",
  "gatsby-plugin-smoothscroll": "^1.0.4",
  "gatsby-plugin-styled-components": "^3.1.16",
  "gatsby-plugin-transition-link": "^1.17.7",
  "gatsby-plugin-typography": "^2.3.20",
  "gatsby-source-contentful": "^2.1.73",
  "gatsby-source-filesystem": "^2.1.40",
  "gatsby-transformer-sharp": "^2.3.7",
  "gh-pages": "^2.2.0",
  "gsap": "^3.0.5",
  "intersection-observer": "^0.7.0",
  "node-sass": "^4.13.0",
  "polished": "^3.4.2",
  "prop-types": "^15.7.2",
  "react": "^16.12.0",
  "react-dom": "^16.12.0",
  "react-formio": "^4.2.4",
  "react-grid-system": "^6.0.7",
  "react-helmet": "^6.0.0-beta",
  "react-hook-form": "^4.9.6",
  "react-icons": "^3.8.0",
  "react-intersection-observer": "^8.25.2",
  "react-player": "^1.15.2",
  "react-redux": "^7.1.3",
  "react-remove-scroll": "^2.2.0",
  "react-scroll": "^1.7.15",
  "react-scroll-to": "^3.0.0-beta.3",
  "react-scrollmagic": "^2.1.1",
  "react-slick": "^0.25.2",
  "react-typography": "^0.16.19",
  "redux": "^4.0.5",
  "styled-components": "^4.4.1",
  "typography": "^0.16.19",
  "uuid": "^3.4.0"
},
3
What happens if you add all of the missing properties?ksav
@ksav perhaps there is a misunderstanding - the missing properties are exactly what I am trying to add via React Helmet in seo.js above. It is those properties that FB scraper does not pick up properly.MonkishTypist

3 Answers

3
votes

We've stumbled upon this issue recently. We've fixed this, please check if:

  • You have updated to the latest version of gatsby-plugin-react-helmet
  • You are using version 6.0.0-beta or later of react-helmet
  • You are importing React Helmet using import { Helmet } from 'react-helmet' rather than the old import Helmet from 'react-helmet'
0
votes

Have you tried something similar? It seems that your Helmet component is closed before setting up your metadata.

   <Helmet
      htmlAttributes={{
        lang,
      }}
      title={title}
      titleTemplate={`%s | ${site.siteMetadata.title}`}
      meta={[
        {
          name: `description`,
          content: metaDescription,
        },
        {
          property: `og:title`,
          content: metaTitle,
        },
        {
          property: `og:description`,
          content: metaDescription,
        },
        {
          property: `og:url`,
          content: metaUrl,
        },
        {
          property: `og:image`,
          content: image,
        },
        {
          property: `og:type`,
          content: post ? `article` : `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: site.siteMetadata.author,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:image`,
          content: image,
        },
        {
          name: `twitter:description`,
          content: metaDescription,
        },
      ].concat(meta)}
    />
  )
}
0
votes

I think Google and Facebook both have issues with helmet because of the extra property data-react-helmet that helmet add, see: https://github.com/nfl/react-helmet/issues/79

to solve this issue you can add this code to your gatsby-ssr.js

    const _ = require("lodash");

    // cleanup html before render
    exports.onPreRenderHTML = ({ getHeadComponents, replaceHeadComponents }) => {
      const headComponents = getHeadComponents();
      replaceHeadComponents(omitDeep(headComponents, ["data-react-helmet"]));
    };

    /**
     * remove properties from collection deep
     * @param collection
     * @param excludeKeys
     * @returns {any}
     */
    const omitDeep = (collection, excludeKeys) =>
      _.cloneDeepWith(collection, value => {
        if (value && typeof value === "object") {
          for (const key of excludeKeys) {
            try {
              delete value[key];
            } catch (_) {
              // console.log("ignore", _);
            }
          }
        }
      });