GatsbyJS Adding a SEO Component

[Fuente: https://www.gatsbyjs.com/docs/add-seo-component/https://www.gatsbyjs.com/docs/use-static-query/]

Adding an SEO Component

Every site on the web has basic meta-tags like the title, favicon or description of the page in their <head> element. This information gets displayed in the browser and is used when someone shares your website, e.g. on Twitter. You can give your users and these websites additional data to embed your website with more data — and that’s where this guide for a SEO component comes in. At the end you’ll have a component you can place in your layout file and have rich previews for other clients, smartphone users, and search engines.

Note: This component will use useStaticQuery. If you’re unfamiliar with that, have a look at the useStaticQuery documentation. You also have to have react-helmet installed for which you can have a look at this document.

Querying Data in Components with the useStaticQuery Hook

Gatsby v2.1.0 introduces useStaticQuery, a new Gatsby feature that provides the ability to use a React Hook to query with GraphQL at build time.

Just like the StaticQuery component, it allows your React components to retrieve data via a GraphQL query that will be parsed, evaluated, and injected into the component. However, useStaticQuery is a hook rather than a component that takes a render prop!

In this guide, you will walk through an example using useStaticQuery. If you’re not familiar with static queries in Gatsby, you might want to check out the difference between a static query and a page query.

How to use useStaticQuery in components

💡 You’ll need React and ReactDOM 16.8.0 or later to use useStaticQuery.

📦 npm install react@^16.8.0 react-dom@^16.8.0

useStaticQuery is a React Hook. All the Rules of Hooks apply.

It takes your GraphQL query and returns the requested data. That’s it!

Basic example

Let’s create a Header component that queries for the site title from gatsby-config.js:

src/components/header.js

import React from "react"
import { useStaticQuery, graphql } from "gatsby"

export default function Header() {
  const data = useStaticQuery(graphql`
    query HeaderQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

  return (
    <header>
      <h1>{data.site.siteMetadata.title}</h1>
    </header>
  )
}

Composing custom useStaticQuery hooks

One of the most compelling features of hooks is the ability to compose and re-use these blocks of functionality. useStaticQuery is a hook. Therefore, using useStaticQuery allows us to compose and re-use blocks of reusable functionality. Perfect!

A classic example is to create a useSiteMetadata hook which will provide the siteMetadata to be re-used in any component. It looks something like:

src/hooks/use-site-metadata.js

import { useStaticQuery, graphql } from "gatsby"

export const useSiteMetadata = () => {
  const { site } = useStaticQuery(
    graphql`
      query SiteMetaData {
        site {
          siteMetadata {
            title
            siteUrl
            headline
            description
            image
            video
            twitter
            name
            logo
          }
        }
      }
    `
  )
  return site.siteMetadata
}

Then import your newly created hook, like so:

src/pages/index.js

import React from "react"
import { useSiteMetadata } from "../hooks/use-site-metadata"

export default function Home() {
  const { title, siteUrl } = useSiteMetadata()
  return <h1>welcome to {title}</h1>
}

Known Limitations

  • useStaticQuery does not accept variables (hence the name “static”), but can be used in any component, including pages
  • Because of how queries currently work in Gatsby, we support only a single instance of useStaticQuery in a file

gatsby-config.js

Gatsby automatically exposes the siteMetadata section of the gatsby-config file in the GraphQL datalayer. It’s considered best practice to place your site meta information there.

gatsby-config.js

module.exports = {
  siteMetadata: {
    title: "Severus Snape",
    titleTemplate: "%s · The Real Hero",
    description:
      "Hogwarts Potions master, Head of Slytherin house and former Death Eater.",
    url: "https://www.doe.com", // No trailing slash allowed!
    image: "/images/snape.jpg", // Path to your image you placed in the 'static' folder
    twitterUsername: "@occlumency",
  },
}

SEO component

First create a new component with this initial boilerplate.

src/components/seo.js

import React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, article }) => {}

export default SEO

SEO.propTypes = {
  title: PropTypes.string,
  description: PropTypes.string,
  image: PropTypes.string,
  article: PropTypes.bool,
}

SEO.defaultProps = {
  title: null,
  description: null,
  image: null,
  article: false,
}

Note: propTypes are included in this example to help you ensure you’re getting all the data you need in the component, and to help serve as a guide while destructuring / using those props.

As the SEO component should also be usable in other files, e.g. a template file, the component also accepts properties for which you set sensible defaults in the SEO.defaultProps section. This way the information you put into siteMetadata gets used every time unless you define the property explicitly.

Now define the query and pass it to useStaticQuery. You can also alias query items, so title gets renamed to defaultTitle.

src/components/seo.js

const SEO = ({ title, description, image, article }) => {
  const { location } = useLocation()
  const { site } = useStaticQuery(query)

  return null
}

export default SEO

const query = graphql`
  query SEO {
    site {
      siteMetadata {
        defaultTitle: title
        titleTemplate
        defaultDescription: description
        siteUrl: url
        defaultImage: image
        twitterUsername
      }
    }
  }
`

The next step is to destructure the data from the query and create an object that checks if the props were used. If not, the default values are applied. Aliasing the properties comes in handy here to avoid name collisions.

src/components/seo.js

const SEO = ({ title, description, image, article }) => {
  const { pathname } = useLocation()
  const { site } = useStaticQuery(query)

  const {
    defaultTitle,
    titleTemplate,
    defaultDescription,
    siteUrl,
    defaultImage,
    twitterUsername,
  } = site.siteMetadata

  const seo = {
    title: title || defaultTitle,
    description: description || defaultDescription,
    image: `${siteUrl}${image || defaultImage}`,
    url: `${siteUrl}${pathname}`,
  }

  return null
}

export default SEO

The last step is to return this data with the help of Helmet. Your complete SEO component should look like this.

src/components/seo.js

import React from "react"
import PropTypes from "prop-types"
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, article }) => {
  const { pathname } = useLocation()
  const { site } = useStaticQuery(query)

  const {
    defaultTitle,
    titleTemplate,
    defaultDescription,
    siteUrl,
    defaultImage,
    twitterUsername,
  } = site.siteMetadata

  const seo = {
    title: title || defaultTitle,
    description: description || defaultDescription,
    image: `${siteUrl}${image || defaultImage}`,
    url: `${siteUrl}${pathname}`,
  }

  return (
    <Helmet title={seo.title} titleTemplate={titleTemplate}>
      <meta name="description" content={seo.description} />
      <meta name="image" content={seo.image} />

      {seo.url && <meta property="og:url" content={seo.url} />}

      {(article ? true : null) && <meta property="og:type" content="article" />}

      {seo.title && <meta property="og:title" content={seo.title} />}

      {seo.description && (
        <meta property="og:description" content={seo.description} />
      )}

      {seo.image && <meta property="og:image" content={seo.image} />}

      <meta name="twitter:card" content="summary_large_image" />

      {twitterUsername && (
        <meta name="twitter:creator" content={twitterUsername} />
      )}

      {seo.title && <meta name="twitter:title" content={seo.title} />}

      {seo.description && (
        <meta name="twitter:description" content={seo.description} />
      )}

      {seo.image && <meta name="twitter:image" content={seo.image} />}
    </Helmet>
  )
}

export default SEO

SEO.propTypes = {
  title: PropTypes.string,
  description: PropTypes.string,
  image: PropTypes.string,
  article: PropTypes.bool,
}

SEO.defaultProps = {
  title: null,
  description: null,
  image: null,
  article: false,
}

const query = graphql`
  query SEO {
    site {
      siteMetadata {
        defaultTitle: title
        titleTemplate
        defaultDescription: description
        siteUrl: url
        defaultImage: image
        twitterUsername
      }
    }
  }
`

Examples

You could also put the Facebook and Twitter meta-tags into their own components, add custom favicons you placed in your static folder, and add schema.org data (Google will use that for their Structured Data). To see how that works you can have a look at these two examples:

As mentioned at the beginning you are also able to use the component in templates, like in this example.

Adding Page Metadata

If you’ve run an audit with Lighthouse, you may have noticed a lackluster score in the “SEO” category. Let’s address how you can improve that score.

Adding metadata to pages (such as a title or description) is key in helping search engines like Google understand your content, and decide when to surface it in search results.

React Helmet is a package that provides a React component interface for you to manage your document head.

Gatsby’s react helmet plugin provides drop-in support for server rendering data added with React Helmet. Using the plugin, attributes you add to React Helmet will be added to the static HTML pages that Gatsby builds.

Using React Helmet and gatsby-plugin-react-helmet

  1. Install both packages: npm install gatsby-plugin-react-helmet react-helmet 
  2. Add the plugin to the plugins array in your gatsby-config.js file.
  3. gatsby-config.js
  4. {
      plugins: [`gatsby-plugin-react-helmet`]
    }
  5. Use React Helmet in your pages:
  6. import React from "react"
    import { Helmet } from "react-helmet"
    
    class Application extends React.Component {
      render() {
        return (
          <div className="application">
            <Helmet>
              <meta charSet="utf-8" />
              <title>My Title</title>
              <link rel="canonical" href="http://mysite.com/example" />
            </Helmet>
          </div>
        )
      }
    }