How to create canonical URLs in Gatsby?
Source: Dev.to

Introduction
In one of my previous posts I described what a canonical URL is. Now, let me show you how to implement this in Gatsby.
Gatsby
What is Gatsby?
Gatsby is a React‑based open‑source framework for creating websites and apps.
- It is a static site generator (SSG) built on React.
- It pre‑renders your React components into HTML files.
- It provides a plugin ecosystem and a lively community.
After Gatsby renders your components into HTML pages you can serve them as a static website on any hosting you want.
React Helmet
What is React Helmet?
React Helmet is a reusable React component that lets you manage all of your <head> tags in a Gatsby project.
import React from "react";
import { Helmet } from "react-helmet";
class Application extends React.Component {
render() {
return (
<Helmet>
<title>My Title</title>
{/* other head tags */}
</Helmet>
);
}
}
When Gatsby renders the HTML you will get:
<head>
<title>My Title</title>
<!-- other head tags -->
</head>
Installation
npm install gatsby-plugin-react-helmet react-helmet
gatsby-plugin-react-helmet-canonical-urls
React Helmet works great, but it conflicts with gatsby-plugin-canonical-urls. Using both can result in two canonical <link> tags.
The gatsby-plugin-react-helmet-canonical-urls plugin solves this by allowing default canonical tags that can be overridden via React Helmet.
Installation
npm install --save gatsby-plugin-react-helmet gatsby-plugin-react-helmet-canonical-urls
Configuration (gatsby-config.js)
// gatsby-config.js
module.exports = {
plugins: [
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-plugin-react-helmet-canonical-urls`,
options: {
siteUrl: "https://www.example.com",
},
},
],
};
See the full list of options here: .
Using Canonical URLs on Static Pages
Example Component (about.jsx)
import React from "react";
import { Helmet } from "react-helmet";
import Layout from "../components/layout";
export default ({ data, location }) => (
<Layout>
<Helmet>
<title>{`${data.site.siteMetadata.title} - About`}</title>
<link
rel="canonical"
href={`${data.site.siteMetadata.siteUrl}${location.pathname}`}
/>
</Helmet>
<h1>About me</h1>
{/* page content */}
</Layout>
);
Site Metadata (gatsby-config.js)
module.exports = {
siteMetadata: {
title: "Kode Skills",
siteUrl: "https://kodeskills.com/",
},
};
GraphQL Query
export const query = graphql`
{
site {
siteMetadata {
title
siteUrl
}
}
}
`;
The location prop (see Gatsby docs) is automatically passed to page components, allowing you to build the full canonical URL from siteUrl and location.pathname.
Conclusion
By combining React Helmet with gatsby-plugin-react-helmet-canonical-urls, you can:
- Define a default canonical URL for the whole site.
- Override it on a per‑page basis when needed.
- Keep your
<link rel="canonical">tags clean and SEO‑friendly.
Happy building! 🚀
How to Create Canonical URLs in Gatsby
What is a canonical URL?
A canonical URL tells search engines which version of a page is the “master” version when there are multiple URLs with the same (or very similar) content. It prevents duplicate‑content penalties and consolidates link equity.
In Gatsby you can add a <link rel="canonical"> tag to the <head> of each page. The value should be the full URL of the page (including protocol and domain).
Static Pages
For a static page you can build the canonical URL from the site’s base URL (siteUrl) and the page’s pathname (location.pathname).
// src/pages/about.jsx
import React from "react";
import { graphql } from "gatsby";
import { Helmet } from "react-helmet";
export default ({ data, location }) => (
<>
<Helmet>
<title>{data.site.siteMetadata.title} – About</title>
<link
rel="canonical"
href={`${data.site.siteMetadata.siteUrl}${location.pathname}`}
/>
</Helmet>
{/* page content */}
</>
);
export const query = graphql`
{
site {
siteMetadata {
title
siteUrl
}
}
}
`;
siteUrlcomes fromgatsby-config.js.location.pathnameis the part of the URL after the domain (e.g./about).
Dynamic Templates
When you have a template that is reused for many pieces of content (blog posts, tag pages, etc.) you can pull the slug from the Markdown front‑matter and construct the canonical URL in the same way.
1. Add a slug to the front‑matter
---
slug: "how-to-create-canonical-urls-in-gatsby"
title: "How to create canonical URLs in Gatsby?"
---
2. Query the data
# src/templates/blog-post.jsx
export const query = graphql`
query BlogPostQuery($slug: String!) {
site {
siteMetadata {
siteUrl
}
}
markdownRemark(frontmatter: { slug: { eq: $slug } }) {
frontmatter {
slug
title
}
}
}
`;
3. Use the data in the component
// src/templates/blog-post.jsx
import React from "react";
import { Helmet } from "react-helmet";
export default ({ data }) => (
<>
<Helmet>
<title>{data.markdownRemark.frontmatter.title}</title>
<link
rel="canonical"
href={`${data.site.siteMetadata.siteUrl}/${data.markdownRemark.frontmatter.slug}`}
/>
</Helmet>
{/* the content of your blog post */}
</>
);
Reusable SEO Component
To avoid repeating the same <Helmet> logic, create a small Seo component that you can drop into any page or template.
// src/components/Seo.jsx
import React from "react";
import { Helmet } from "react-helmet";
export const Seo = ({ canonicalUrl, siteTitle, title }) => (
<Helmet>
<title>{siteTitle} – {title}</title>
{canonicalUrl && <link rel="canonical" href={canonicalUrl} />}
</Helmet>
);
Use in a static page
// src/pages/about.jsx
import React from "react";
import { graphql } from "gatsby";
import { Seo } from "../components/Seo";
export default ({ data, location }) => (
<>
<Seo
siteTitle={data.site.siteMetadata.title}
title="About"
canonicalUrl={`${data.site.siteMetadata.siteUrl}${location.pathname}`}
/>
{/* page content */}
</>
);
export const query = graphql`
{
site {
siteMetadata {
title
siteUrl
}
}
}
`;
Use in a dynamic blog‑post template
// src/templates/blog-post.jsx
import React from "react";
import { graphql } from "gatsby";
import { Seo } from "../components/Seo";
export default ({ data }) => (
<>
<Seo
siteTitle={data.site.siteMetadata.title}
title={data.markdownRemark.frontmatter.title}
canonicalUrl={`${data.site.siteMetadata.siteUrl}/${data.markdownRemark.frontmatter.slug}`}
/>
{/* post content */}
</>
);
export const query = graphql`
query BlogPostQuery($slug: String!) {
site {
siteMetadata {
title
siteUrl
}
}
markdownRemark(frontmatter: { slug: { eq: $slug } }) {
frontmatter {
slug
title
}
}
}
`;
Conclusion
Setting up canonical URLs is one of the first SEO steps you should take for any website. It’s especially important when you syndicate content to other platforms (Medium, dev.to, etc.).
With Gatsby you can generate canonical URLs automatically for both static pages and dynamic templates, and a small reusable Seo component keeps your code DRY and easy to maintain.
