Skip to content

Building SEO-Powered Websites With Gatsby

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Gatsby is a framework that leverages React for building SEO-powered websites. Many websites are created to be discovered, hence, SEO support is an important factor in such websites.

Many factors influence SEO such as accessibility, correct meta-information (at the head tag) and some other external tools. Gatsby supports using appropriate meta information for individual pages to improve their presence online.

In this article, we'll look at the limitations of Create React App in respect to SEO, and how Gatsby solves this with SSR. Furthermore in the article, we'll go through a tutorial on building a basic website with good SEO.

Why CRA is not a good tool for SEO

CRA is an opinionated tool used for building React applications but cannot be used for SEO. Here's why:

When using React, you'll most probably be using a library like react-helmet (a document head manager for React) for updating meta-information about the site. The limitations of such libraries is that they contain JavaScript, which means they can only be executed on the browser (so JavaScript can run).

SEO crawlers or social media tools that inspect head tags of websites (to display a card, perhaps) would not execute that JavaScript. Instead, they make use of the default meta information they can find. In CRA, the default title in public/index.html is "React App". This means, for every page you create (based on routes), they will all have the same title. They only show different titles when they are executed on the client's browser because the react-helmet library gets the chance to be executed, thereby updating the head tags. This article contains more information.

How Gatsby solves React SEO problems with SSR

Gatsby is a Static Site Generator (SSG) which uses Server Side Rendering (SSR) to generate static pages during the build process.

What this means is that you provide dynamic meta information in every page, and during the process of static site generation, pages are server-side rendered with the specified meta information, thereby making static pages with their own details.

With this technique, every page contains its own meta title, meta description and basically every meta information.

The following tutorial shows how Gatsby improves SEO in web applications.

Building an SEO powered site with Gatsby

We'll be building a basic website with two pages: / - Home and /about - About Us. These two pages would have their own meta information attached to them during the build process.

To get started, let's created our Gatsby project. Run the following in your terminal:

gatsby new new-project https://github.com/gatsbyjs/gatsby-starter-default

This pulls the default template, and installs all necessary dependencies. In the src directory, you'll find three directories: components, images and pages.

As you may observe, the template already comes with some configurations for seo and optimizing images. To build our project afresh, delete the following files/directories:

components/header.js
components/layout.css
components/layout.js
components/image.js
pages/404.js
pages/page-2.js
pages/index.js
pages/using-typescript.tsx

This will leave us with components/seo.js and images.

In a future series, we'll explore the gatsby-image plugin used in components/images.js. But for now, understand that it performs optimizations on images.

Let's briefly explore the content of components/seo.js

function SEO({ description, lang, meta, title }) {
	const { site } = useStaticQuery(
		graphql`
			query {
				site {
					siteMetadata {
						title
						description
						author
					}
				}
			}
		`
	);

	const metaDescription = description || site.siteMetadata.description;
	const defaultTitle = site.siteMetadata?.title;

	return (
		<Helmet
			htmlAttributes={{
				lang,
			}}
			title={title}
			titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : null}
			meta={[
				{
					name: `description`,
					content: metaDescription,
				},
				{
					property: `og:title`,
					content: title,
				},
				{
					property: `og:description`,
					content: metaDescription,
				},
				{
					property: `og:type`,
					content: `website`,
				},
				{
					name: `twitter:card`,
					content: `summary`,
				},
				{
					name: `twitter:creator`,
					content: site.siteMetadata?.author || ``,
				},
				{
					name: `twitter:title`,
					content: title,
				},
				{
					name: `twitter:description`,
					content: metaDescription,
				},
			].concat(meta)}
		/>
	);
}

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

SEO.propTypes = {
	description: PropTypes.string,
	lang: PropTypes.string,
	meta: PropTypes.arrayOf(PropTypes.object),
	title: PropTypes.string.isRequired,
};

Note that this component can look a bit different in another template, or you may do it differently.

The SEO component receives four props: title, description, lang and meta with title as required. You can specify more props if you want or take out those you don't need.

This allows different pages to specify their titles, descriptions and other meta information specific to them.

Helmet is from react-helmet but is used a bit different from how it is used in CRA. It works with gatsby-plugin-react-helmet which provides server rendering support.

components/seo.js also contains some GraphQL which we will cover in a future series.

The Helmet plugin during the build process populates all pages with their respective meta information depending on the inputs provided during development.

Now let's add our pages.

With Gatsby, you do not need any routing packages for determining components to show based on specific URLs. To create a page, all you need to do is add the component's file directly under the pages directory.

To create the two pages for our project, add two files: index.js for / and about.js for /about.

Before proceeding with our pages, let's add a layout.

Create components/layout.js and components/header.js.

Add the following in components/header.js:

import { Link } from "gatsby";
import React from "react";
import PropTypes from "prop-types";

const Header = ({ siteName }) => {
	return (
		<header className="header">
			<div className="site">
				<Link to="/">{siteName}</Link>
			</div>
			<nav>
				<ul>
					<li>
						<Link to="/">Home</Link>{" "}
					</li>
					<li>
						<Link to="/about">About</Link>{" "}
					</li>
				</ul>
			</nav>
		</header>
	);
};

Header.propTypes = {
	siteName: PropTypes.string.isRequired,
};

export default Header;

Same React. The only thing new here is a different Link component from Gatsby is used.

In the components/layout.js, add the following:

import React from "react";
import Header from "./header";

const Layout = ({ children }) => {
	return (
		<div>
			<Header siteName="My Project" />
			{children}
		</div>
	);
};

export default Layout;

For the pages, add the following to index.js:

import React from "react";
import { Link } from "gatsby";

import Layout from "../components/layout";
import SEO from "../components/seo";

const IndexPage = () => (
	<Layout>
		<SEO title="Homepage" description="This is the homepage of my website" />
		<div className="hero-container">
			<div className="hero-left">
				<span className="hero-left-heading">Buy your Laptops Here</span>
				<span className="hero-left-text">
					You can get quality laptops here. What are you waiting for to make
					your first purchase?
				</span>
				<Link className="hero-left-link" to="/about">
					Learn More about me
				</Link>
			</div>
			<div className="hero-right">
				<div className="hero-right-image-container">
					<img src={require("../images/laptop.jpg")} />
				</div>
			</div>
		</div>
	</Layout>
);

export default IndexPage;

I added an unsplash image to images and required it require('../images/laptop.jpg') as seen above.

We'll look at the usage of the SEO component soon.

For pages/about.js, add the following:

import React from "react";

import Layout from "../components/layout";
import SEO from "../components/seo";

const AboutPage = () => (
	<Layout>
		<SEO
			title="About my website"
			description="This page contains more information about my website and the work that I do"
		/>
		<main>
			<div className="about-container">
				<h1>About Me</h1>
				<p>I sell quality computers!! Okay?</p>
			</div>
		</main>
	</Layout>
);

export default AboutPage;

Create a new directory called styles under src and create a new file: global.css. Copy the following css styles to that file:

* {
	box-sizing: border-box;
}

body {
	margin: 0;
	padding: 0;
}

ul {
	padding: 0;
	list-style: none;
}

.header {
	padding: 20px;
	background-color: rgb(5, 42, 123);
	display: flex;
	justify-content: space-between;
	align-items: center;
}

.header .site a {
	color: white;
	font-size: 30px;
	text-decoration: none;
}

.header nav ul {
	display: flex;
}
.header nav ul li a {
	color: white;
	margin: 20px;
}

.hero-container {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 100px 30px;
}

.hero-left {
	display: flex;
	flex-direction: column;
	max-width: 500px;
	padding: 20px;
}

.hero-left-heading {
	font-size: 40px;
	color: rgb(181, 0, 154);
	font-weight: bold;
}

.hero-left-text {
	margin: 20px 0;
}

.hero-left-link {
	display: block;
	width: max-content;
	padding: 15px;
	border-radius: 5px;
	background-color: rgb(181, 0, 154);
	color: white;
	display: flex;
	justify-content: center;
	align-items: center;
	text-decoration: none;
}

.hero-right-image-container {
	width: 600px;
	height: 400px;
	overflow: hidden;
}
.hero-right-image-container img {
	object-fit: cover;
	width: 100%;
	height: 100%;
}

.about-container {
	margin-top: 100px;
	text-align: center;
}

.about-container h1 {
	font-size: 50px;
}

For the global stylesheet to be used for the whole site, the gatsby-browser.js API file would be used.

gatsby-browser.js is a reserved API file that gives access to actions within the browser.

In gatsby-browser.js (at the root of your project), add the following:

import "./src/styles/global.css";

When you run the gatsby server for your project (gatsby develop), you'll get the following on localhost:8000:

For /:

Gatsby Basic Website Homepage

For /about:

Gatsby Basic Website About page

The SEO component makes all the pages unique and SEO-ready. For index.js, we have:

<SEO title="Homepage" description="This is the homepage of my website" />

Just as we have configured the SEO component using react-helmet, this updates the meta information for the homepage during the build process. This way, the first thing crawlers will see is the unique meta details for the page, as they do not require any JavaScript to be updated.

To test this, do the following:

  • build for project for production (gatsby run build)
  • serve the production build (gatsby run serve)

This will run the built content on localhost:9000.

You can use curl on your terminal to inspect the source code (or run inspect on the browser).

curl localhost:9000

The result:

Gatsby Basic Website Homepage Source

The reason it came out as "Homepage | Gatsby Default Starter" is because of the prop titleTemplate provided by Helmet which was configured like so in the SEO template:

titleTemplate={defaultTitle ? `%s | ${defaultTitle}` : null}

This appends a default title to every title provided by the pages.

Conclusion

In this article, we looked at how Gatsby solves the problem of SEO using server side rendering for generating static pages.

The basic example used in the tutorial shows how each page contain their own meta information that can easily be crawled by SEO bots or social media tools.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co