Gatsby SEO Component
In this post i’m going to explain how to create an Seo component for use in a Gatsby application or site, I seem to always end up writing this over and over again so first and foremost this is for my reference but if you’re interested read on.
For the component to work we’ll also need to install react-helment and a Gatsby plugin gatsby-plugin-react-helmet
Install the following dependencies to get started 👇
npm install react-helmet gatsby-plugin-react-helmet --save
Then open up your gatsby-config.js
and add the plugin to the plugins array
// gatsby-config.js
module.exports = {
plugins: [`gatsby-plugin-react-helmet`],
};
While we’re in gatsby-config.js
have a look over what keys you have in you siteMetadata
, this is a standard one I’ve
used a few times and has most of what you’ll need to pass on to the Seo component when we build it in a moment.
// gatsby-config.js
module.exports = {
+ siteMetadata: {
+ title: 'Mr Website',
+ description: 'A website about Mr Website',
+ keywords: ['React', 'Gatsby', 'Jamstack'],
+ url: 'https://gatsby-seo-component.netlify.app',
+ ogImage: 'images/og-image.jpg',
+ favicon: {
+ ico: 'images/favicon.ico',
+ sm: 'images/favicon-16x16.png',
+ lg: 'images/favicon-32x32.png'
+ },
+ lang: `en`
+ },
plugins: [`gatsby-plugin-react-helmet`],
}
I’d like to point one thing out here and that’s the path to the .jpg
and .ico
, .png
files. Those paths relate to
an images
directory that I’ve created in ./static
. The static directory is where Gatsby looks to load statically
served content. For the above to work your directory structure would probably look a bit like this 👇
src
|-- pages
|-- components
|-- utils
static
|-- images
gatsby-config.js
package.json
No we can build the component.
// src/components/seo.js
import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
export const Seo = ({ title, description, keywords, url, ogImage, favicon, lang, type, page, path }) => {
return (
<Helmet>
<title>{`${title} | ${page}`}</title>
<html lang={lang} />
<meta name='description' content={description} />
<meta name='image' content={`${url}/${ogImage}`} />
<meta name='image:alt' content={description} />
<meta name='keywords' content={keywords.join(', ')} />
<link rel='icon' type='image/png' sizes='16x16' href={`${url}/${favicon.ico}`} />
<link rel='icon' type='image/png' sizes='16x16' href={`${url}/${favicon.sm}`} />
<link rel='icon' type='image/png' sizes='32x32' href={`${url}/${favicon.lg}`} />
{/* Facebook */}
<meta property='og:type' content={type} />
<meta property='og:title' content={page} />
<meta property='og:url' content={`${url}${path}`} />
<meta property='og:description' content={description} />
<meta property='og:image' content={`${url}/${ogImage}`} />
<meta property='og:image:alt' content={description}></meta>
{/* Twitter */}
<meta name='twitter:card' content='summary_large_image' />
<meta name='twitter:title' content={page} />
<meta name='twitter:url' content={`${url}${path}`} />
<meta name='twitter:description' content={description} />
<meta name='twitter:image' content={`${url}/${ogImage}`} />
<meta name='twitter:image:alt' content={description}></meta>
</Helmet>
);
};
Seo.propTypes = {
/** The site title */
title: PropTypes.string.isRequired,
/** The site description */
description: PropTypes.string.isRequired,
/** Keywords to use in meta keywords */
keywords: PropTypes.arrayOf(PropTypes.string),
/** The site URL */
url: PropTypes.string.isRequired,
/** Image url to use for opengraph image */
ogImage: PropTypes.string,
/** Favicon image urls**/
favicon: PropTypes.shape({
ico: PropTypes.string,
sm: PropTypes.string,
lg: PropTypes.string,
}),
/** Lang to use as meta lang */
lang: PropTypes.string.isRequired,
/** The type of meta - useful for Facebook */
type: PropTypes.oneOf(['website', 'article']).isRequired,
/** The page name */
page: PropTypes.string.isRequired,
/** The path to the page */
path: PropTypes.string.isRequired,
};
You should be able to see from each of the prop descriptions what each one does and from title
to lang
you’ll be
using the values from siteMetadata
… more on that in a moment.
type
, page
and path
will most likely be unique to each page of your site so we won’t pull this out of the
siteMetadata
instead I’ll show you how to pass those in as props… more on that in moment too.
There’s now two key steps to making this work you can do them in either order.
useSiteMetadata hook
This is a utility function that can be used in conjunction with useStaticQuery
and graphql
from “gatsby”
// src/utils/useSiteMetadata.js
import { useStaticQuery, graphql } from 'gatsby';
export const useSiteMetadata = () => {
return useStaticQuery(
graphql`
query {
site {
siteMetadata {
title
description
keywords
url
ogImage
favicon {
ico
sm
lg
}
lang
}
}
}
`
);
};
Add Seo component to pages
The simplest approach is to import the Seo
component and the useSiteMetadata
hook for each [page].js
from
src/pages
. The better approach is to create a page layout component but I won’t cover that in this post.
To implement the Seo
component each [page].js
might look something like this 👇
// src/pages/index.js
import React, { Fragment } from 'react';
import { Link } from 'gatsby';
import { Seo } from '../components/seo';
import { useSiteMetadata } from '../utils/useSiteMetadata';
const IndexPage = () => {
const {
site: {
siteMetadata: { title, description, keywords, url, ogImage, favicon, lang },
},
} = useSiteMetadata();
return (
<Fragment>
<Seo
title={title}
description={description}
keywords={keywords}
url={url}
ogImage={ogImage}
favicon={favicon}
lang={lang}
type='website'
page='Home'
path=''
/>
<h1>This is index.js</h1>
...
</Fragment>
);
};
export default IndexPage;
I’ve destructured all the props in the above code snippet but you can of course use JavaScript’s native spread operator to achieve the same thing but in the interest of making this post as clear as possible I’ll try and avoid draw the rest of the fucking owl
You should also see near the bottom you can see where i’ve added values for type
, page
and path
type = 'website';
page = 'Home';
path = '';
As always there’s a million ways to do the same thing but I hope this gives you some indication of one way to do it but in most cases you’ll likely need to make a change here and there… and that’s absolutely fine!
SEO Validation
There’s a few ways to test if your meta data is working correctly, the following are two links I use to preview what my meta data will render as if I post a link to either Twitter or Facebook