[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
- Install both packages: npm install gatsby-plugin-react-helmet react-helmet
- Add the plugin to the
plugins
array in yourgatsby-config.js
file. - gatsby-config.js
-
{ plugins: [`gatsby-plugin-react-helmet`] }
- Use
React Helmet
in your pages: -
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> ) } }