Gatsby SEO
How to optimize a Gatsby site for search — Gatsby's four rendering paths (SSG, DSG, SSR, client-only) and what each means for crawlability, the modern Head API vs legacy react-helmet, sitemaps, canonicals, image SEO, the React-bundle CWV cost, and the Netlify-era maintenance risk.
1 evidence signal on this page
- Related live toolCanonicalization Checker
Gatsby has four rendering options: SSG (the default — pages pre-rendered to static HTML at gatsby build), DSG (deferred static generation on first request), SSR (server-side rendering per request via Gatsby Functions), and client-only routes (rendered entirely in the browser). Most Gatsby sites lean on SSG, which gives crawlers fully rendered HTML on the first fetch with no rendering queue to wait on — a strong SEO baseline, far better than a pure client-side React app. But that baseline isn't universal: DSG and SSR pages generate HTML outside the build step and need their own production checks, and client-only routes shouldn't be assumed to expose route-specific content in the initial HTML at all. Whichever path serves a page, Gatsby still ships the full React runtime (~200KB+) and hydrates it client-side — a Core Web Vitals cost, not a crawlability one. The current way to manage metadata is the built-in Gatsby Head API (v4.19+), which works across all four rendering paths and replaces gatsby-plugin-react-helmet. The recurring pitfalls are double canonical tags, draft/orphan pages leaking into sitemaps, missing alt text, the production-only sitemap gotcha, DSG/SSR/client-only routes needing explicit sitemap and production-behavior checks, and a framework whose maintenance has slowed sharply since Netlify's 2023 acquisition.
TL;DR — By default, Gatsby builds your pages into finished HTML before anyone visits (this mode is called SSG), so when Google shows up, your content is already in the page — no waiting for JavaScript. That gives Gatsby a strong SEO head start. Gatsby also has a few other rendering optionsTurning HTML, CSS, and JavaScript into the final visual page and DOM. for pages that need to generate later or use per-request data — worth knowing if your site uses them. The catch: it still sends a big React bundle to the browser, which can slow your page down, and you still have to set up meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored., sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., canonicals, and image alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. yourself.
What Gatsby is
Gatsby is a website framework built on React. Most React apps build the
page in your browser after the JavaScript loads — which is a problem for SEO,
because a search engine fetching the page first sees a near-empty shell. Gatsby
flips that around by default. When you run gatsby build, it turns most pages into
a complete HTML file ahead of time. So when Google, Bing, or a reader requests one
of those pages, the content is right there in the HTML. Evidence for this claim Gatsby creates static HTML files during its production build. Scope: Gatsby static generation; client-side behavior can still be added. Confidence: high · Verified: Gatsby: Builds and deploys
That default mode makes Gatsby a static site generatorA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. (SSG), and it’s why Gatsby is generally good for SEO out of the box — much better than a plain React app. Gatsby also lets individual pages opt into three other renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. modes — generating on first request, generating per-request on a server, or rendering entirely in the browser — which the Advanced tab covers, since each one changes what a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. actually sees.
Why people worry about Gatsby and SEO (and why they mostly shouldn’t)
The most common myth is “Gatsby is bad for SEO because it uses React.” That’s false. The React part runs after the page is already built and delivered — it just makes the page interactive. The text, links, and headings a search engine cares about are already in the HTML from the start.
So Gatsby clears the biggest hurdle automatically. What it doesn’t do is all of SEO for you.
What you still have to set up
A Gatsby site is not automatically optimized. You still need to:
- Add title tagsThe title tag is the HTML title element in a page's head that specifies the document's title. It's the primary source for the SERP title link and a confirmed light ranking factor — but since August 2021 Google doesn't always show it verbatim. and meta descriptionsThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through. to every page (the modern way is the built-in Gatsby Head API). Evidence for this claim Gatsby's Head API lets pages export document-head metadata. Scope: Gatsby Head API. Confidence: high · Verified: Gatsby: Head API
- Generate a sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. (the
gatsby-plugin-sitemapplugin). - Set canonical URLsHow search engines pick one canonical URL among duplicates and consolidate signals onto it. so duplicate versions of a page don’t compete.
- Write alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. for images — Gatsby’s image tool resizes and optimizes images automatically, but it does not write alt text for you.
The one thing that surprises people
Gatsby still ships the full React runtime (~200KB+) to the browser on every page. It doesn’t hurt whether you get indexed — the HTML is already complete — but it can slow your page down, which affects Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. (Google’s page-experience speed metrics). Lighter frameworks like Astro send almost no JavaScript by comparison.
One more thing to know if you’re choosing Gatsby today: Netlify acquired Gatsby in 2023, and active development has slowed a lot since. For an existing Gatsby site that’s fine; for a brand-new project, it’s worth weighing.
Want the technical version — the Head API vs the old react-helmet plugin, the sitemap and canonical gotchas, the React-bundle CWV trade-off, and the honest take on Gatsby’s future? Switch to the Advanced tab.
TL;DR — Gatsby has four rendering optionsTurning HTML, CSS, and JavaScript into the final visual page and DOM. — SSG (the default), DSG, SSR, and client-only routes — and they don’t all put content in the raw HTML the same way. SSG pre-renders to static HTML at build time (
gatsby build), so content is in the raw HTML before the first crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. request — no Web RenderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. Service, no rendering queue. DSG defers generation to the first request; SSR generates per request on a server; client-only routes render nothing route-specific until the browser runs the JavaScript. Whichever path serves a page, Gatsby hydrates a full React bundle (~200KB+) on the client, which is a Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. cost, not a crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index. one. The current metadata approach is the built-in Gatsby Head API (v4.19+), which works across all four rendering paths and replacesgatsby-plugin-react-helmet. The recurring failure modes are double canonical tags (Head API + react-helmet both firing), draft/orphan pagesAn orphan page is a page on your site that no other page links to internally. Because crawlers discover pages by following links, an orphan page is effectively invisible to search engines unless it's in an XML sitemap or linked from an external site. leaking into sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., the production-only sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. gotcha, missing alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. onGatsbyImage, trailing-slash inconsistency, and DSG/SSR/client-only routes that never got a production check. And the elephant in the room: since Netlify’s 2023 acquisition, Gatsby’s maintenance has slowed sharply.
Gatsby’s four rendering options — and why they matter for SEO
Google processes JavaScript pages in three phases — crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., then rendering, then indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. — and rendering happens in a separate pass from a queue using headless Chromium. Google’s own guidance is blunt about why you shouldn’t lean on that: “Server-side or pre-rendering is still a great idea because it makes your website faster for users and crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index., and not all bots can run JavaScript.”
A Gatsby site isn’t universally build-time static HTML — Gatsby supports four distinct rendering paths, chosen per page or template Evidence for this claim gatsby build writes production output, including generated HTML, to the public directory. Scope: Gatsby production builds. Confidence: high · Verified: Gatsby CLI: build :
- SSG (Static Site Generation) — the default.
gatsby buildemits fully rendered static HTML into/publicfor the page. GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer.’s first-wave raw-HTML fetch already contains the complete content — text, links, metadata. This is the safe, low-risk path, and it’s what most Gatsby pages use. - DSG (Deferred Static Generation). Generation is deferred until the page’s first request rather than happening for every page at build time — useful for sites with huge numbers of low-traffic pages where a full build would be slow. The build alone does not tell you what a crawler sees; the page’s HTML doesn’t exist until something requests it, so first-request and cached behavior need their own check, not just a build log.
- SSR (Server-Side Rendering). The page is rendered per request, using request-time data, via Gatsby Functions. Because it runs at request time, SSR pages need production checks a build-time page doesn’t: response status, caching headers, timeout behavior, and what a crawler sees on an empty or error response — none of that is visible from a successful local build.
- Client-only routes. These render entirely in the browser and don’t get route-specific content in the initial HTML — the same profile as a plain client-side React app. Don’t assume Google (or any crawler) sees anything page-specific here until JavaScript runs; treat these as JS-dependent by design, which is fine for gated/authenticated content but wrong for anything you want indexed with content.
React hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. (ReactDOMClient.hydrateRoot()) happens client-side on top of
whichever of these produced the HTML, purely to add interactivity — that part is
the same regardless of rendering path, and is a separate concern from which path
generated the page (more on hydration cost below).
Contrast SSG/DSG/SSR with a pure client-side React app, which serves an empty
<div id="root"> and depends on the browser (or the renderer) to build the page.
Most Gatsby pages ship meaningful HTML by default — that’s the indexability win,
and it’s real, but it’s a per-page property, not a framework guarantee. Rendering
options and image tooling don’t guarantee Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data., indexing, or rankings on
their own; verify the actual production output for whichever path a route uses.
The catch with SSG/DSG/SSR alike is a performance one, not a crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index. one: Gatsby ships the full React runtime to every page and re-hydrates it. More on that, and the Astro comparison, below.
Gatsby Head API vs gatsby-plugin-react-helmet
This is the single most important “are you doing it the modern way?” question in Gatsby SEOGatsby SEO is the set of practices for getting a Gatsby site found and ranked. By default, Gatsby pre-renders HTML at build time (SSG), putting content in the raw HTML on the first fetch — but Gatsby also supports DSG, SSR, and client-only routes that don't all work that way, and it ships a full React bundle, so canonical tags, sitemaps, and Core Web Vitals still need deliberate work..
The legacy approach — gatsby-plugin-react-helmet. For years, the standard way
to set <title>, meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., and other head tags was the react-helmet
library plus this plugin. The plugin’s job was to give react-helmet SSR
support — without it, your title/meta tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored. would only appear after JS execution,
not in the raw HTML, which defeats the point. It works, but it has known issues with
React Hooks and concurrent rendering, plus a background-tab title bug you patch with
defer={false}.
The modern approach — the Gatsby Head API (v4.19+). Gatsby now has a built-in
way to add head elements: a named Head export from any page or template file. Evidence for this claim Gatsby pages and templates can export a named Head function to add head elements. Scope: Gatsby Head API. Confidence: high · Verified: Gatsby: Head API
export const Head = () => (
<>
<title>Page Title</title>
<meta name="description" content="..." />
</>
)It receives useful props — location.pathname, params, data (from the page’s
GraphQL query), and pageContext — and it deduplicates tags that share an id
prop (last one wins), though running two different head-tag mechanisms at once
(Head API plus a leftover react-helmet call) can still conflict even with
deduplication. It works only in page files and templates, not in arbitrary
components. The advantages over react-helmet: no third-party bundle, no Provider
wrapper, deterministic tag order with React 18 streaming. Use the Head API for all
new projects, and plan a migration for existing ones.
The Head API works the same way across all four rendering paths — SSG, DSG, SSR,
and client-only routes all support a Head export. What differs is when its
output lands in HTML a crawler can fetch: on SSG it’s baked in at build time; on
DSG and SSR it’s generated at first-request or per-request time; on a client-only
route it isn’t in the initial HTML at all. Don’t assume “I added a Head export”
is equivalent to “this is in the raw HTML for every route” — check the actual
production output (view-source: or curl) for whichever path each route uses,
not just one representative page.
The “meta tags in DevTools but not in source” bug. A classic Gatsby SEO symptom:
your title/meta tags show up in Chrome DevTools but are missing from view-source:.
The cause is that DevTools shows the hydrated DOM (after JS runs), while view-source
shows the raw HTML. If your tags only appear in DevTools, your SEO component is
rendering client-side instead of being baked into Gatsby’s static output — usually
because it’s used as a regular component rather than as (or inside) the page’s Head
export. Always verify in raw HTML, not DevTools.
The browser DOM can hide a broken production artifact. This compares initial HTML with rendered output and flags titles, descriptions, canonicals, and content that appear only after JavaScript runs.
Test a production route with my free Render Gap Analyzer Free
- Run a production build and test a representative page or template—not gatsby develop.
- Trace any initial-vs-rendered metadata gap back to the Head export or legacy helmet setup.
- Rebuild, retest the raw output, and remove duplicate head-management mechanisms.
The analyzer reports that the title only appears after rendering and says placing it in the initial HTML is more reliable.
Wiring up an SEO component (the GraphQL data layer)
Gatsby’s data layer is GraphQL, and it’s how you feed metadata into your pages.
useStaticQuerypulls global defaults fromsiteMetadata(title, description,siteUrl) defined ingatsby-config.js.- Page-level GraphQL queries pass a
dataprop straight to theHeadexport — no extra wiring needed. - The standard pattern is prop || siteMetadata fallback: use the per-page value if it exists, otherwise the site default.
A Head export that takes page data looks like:
export const Head = ({ data }) => (
<>
<title>{data.post.title}</title>
<meta name="description" content={data.post.excerpt} />
</>
)Sitemaps: gatsby-plugin-sitemap (and its traps)
Install gatsby-plugin-sitemap and configure it in gatsby-config.js. A few things
trip people up:
- It generates
sitemap-index.xml, not/sitemap.xml. Submit the index URL in Google Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results. — don’t submit/sitemap.xmland expect it to resolve. - It only runs in production builds. It does nothing in
gatsby develop. To test it, rungatsby build && gatsby serve. People file “my sitemap is missing” reports that are really just “I never ran a production build.” createLinkInHead: trueby default adds the sitemap reference to the HTML head automatically.- It always excludes
/dev-404-page,/404, and/offline-plugin-app-shell-fallback. <priority>and<changefreq>are ignored by Google — the plugin docs say so directly. Focus on an accurate<lastmod>instead.entryLimitdefaults to 45,000 URLs per file.
Excluding drafts is your job. Gatsby builds everything it finds, so draft
content sails straight into the sitemap unless you filter it. Do that in
gatsby-node.js with a GraphQL filter (e.g. excluding entries without a publish
date), not by hiding it in a React component — by then it’s already built and
listed.
DSG, SSR, and client-only routes need an explicit sitemap decision.
gatsby-plugin-sitemap reflects what it can see at build time. An SSG page is
straightforward — it exists as a static file, so it’s naturally sitemap-eligible.
A DSG page’s HTML doesn’t exist yet at build time (it generates on first request),
an SSR page never has fixed HTML at all, and a client-only route has no
route-specific content to index in the first place. Don’t assume any of these are
in the sitemap (or should be) just because the route exists — decide, page by page,
whether it belongs, and verify the generated sitemap-index.xml actually reflects
that decision rather than a build-time guess.
robots.txt: gatsby-plugin-robots-txt
gatsby-plugin-robots-txt generates robots.txt at build time. The useful detail
for SEO is environment awareness: it reads process.env.GATSBY_ACTIVE_ENV then
process.env.NODE_ENV, so you can serve different rules per environment. The
classic use is blocking crawlers on Netlify preview/branch deploys so your
staging URLs don’t get accidentally indexed, while leaving production open.
Canonical URLs (and the double-canonical bug)
Two viable approaches:
gatsby-plugin-canonical-urlsadds a<link rel="canonical">to every page. SetstripQueryString: trueso/blog?tag=fooand/blogdon’t get treated as separate canonicalized pages — recommended for most sites.- The Head API, setting canonicals yourself from
location.pathname:
export const Head = ({ location }) => (
<link rel="canonical" href={`https://example.com${location.pathname}`} />
)The double canonical bug. This is a known, easy-to-hit issue: if you use
gatsby-plugin-canonical-urls and a react-helmet canonical tagA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. at the same
time, you emit two <link rel="canonical"> tags. Pick one mechanism. (If you’re
on react-helmet, gatsby-plugin-react-helmet-canonical-urls is the helmet-aware
option; on the Head API, set the canonical there and drop the plugin.)
Trailing slashes. Gatsby pages can be reachable with and without a trailing
slash, and Gatsby’s <Link> component uses client-side History API routing — which
bypasses server-side 301 redirectsA 301 redirect is the HTTP status code for a permanent move: it tells browsers and search engines a URL has moved for good, and it's the strongest signal for consolidating a page's ranking signals onto the new URL. Google says permanent redirects don't cause a loss in PageRank. you’d normally use to normalize trailing slashes.
Decide on one form, enforce it at the host/CDN level, and keep canonicals consistent
with it.
Image SEO: gatsby-plugin-image
gatsby-plugin-image is genuinely one of Gatsby’s strengths. Two components:
StaticImage— for images whose path is known and hardcoded at build time.GatsbyImage— for dynamic images coming from GraphQL.
What it does automatically: multiple sizes, WebP/AVIF formats, lazy loadingLazy loading defers loading of off-screen or non-critical resources — usually images and iframes — until they're about to enter the viewport. The native way to do it is the loading=\"lazy\" HTML attribute, which needs no JavaScript., and breakpoints (750/1080/1366/1920px). It also generates placeholders (blurred, dominant color, or traced SVG) that reserve space and prevent Cumulative Layout Shift — a Core Web VitalsWeb Vitals is Google's initiative (launched May 2020) for unified page-experience quality signals. Core Web Vitals — LCP, INP, and CLS — are the subset used in ranking; the rest (TTFB, FCP, TBT, Speed Index) are diagnostic, not ranking factors. metric. Because dimensions are set, you avoid CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good., and modern formats plus lazy loading help LCPLargest Contentful Paint — render time of the largest visible image or text block, relative to when the page started loading. ≤2.5 s (at the 75th percentile) is good..
The one thing it does not do is write alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search.. That’s on you, every time —
missing alt text on GatsbyImage is one of the most common Gatsby SEO oversights.
(Migrating from the old gatsby-image package? There’s a codemod:
npx gatsby-codemods gatsby-plugin-image.)
Structured data (JSON-LD)
Google’s preferred structured-data format is JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal., and the clean way to add it in modern Gatsby is via the Head API with a script tag:
export const Head = ({ data }) => (
<script type="application/ld+json">
{JSON.stringify({
"@context": "https://schema.org",
"@type": "Article",
"headline": data.post.title,
})}
</script>
)gatsby-plugin-next-seo offers pre-built JSON-LD components if you’d rather not
hand-roll them. One frequent confusion to clear up: gatsby-plugin-manifest is
not a structured-data plugin — it generates the PWA web app manifest (icons,
theme color), nothing to do with schema.
The React bundle and Core Web Vitals
Here’s Gatsby’s real weakness relative to the zero-JS generators.
- Full hydration (the Gatsby 1–4 default) hydrates the entire React tree and ships a 200KB+ React runtime to every page. The HTML is pre-rendered, so this doesn’t hurt crawlability — but it absolutely affects load speed and CWV.
- Partial hydration (Gatsby 5, experimental) hydrates only components marked
"use client"and leaves the rest as static HTML, cutting the JS shipped and directly improving TTI and CWV. The limitations are real: production builds only, still beta, and incompatible with emotion, styled-components, andgatsby-plugin-offline.
The takeaway: Gatsby’s JS payload is a performance problem, not an indexability one. Googlebot still renders JS to assess page-experience signals, so the bundle can cost you on CWV even though your content indexes fine.
Gatsby vs Astro for SEO
If you’re choosing a static framework today, this is the comparison that matters most for SEO.
| Dimension | Gatsby | Astro |
|---|---|---|
| JS shipped to browser | 200KB+ (full React runtime) | ~5KB (interactive islands only) |
| Rendering model | SSG → SPA (full hydration) | SSG → MPA (zero hydration by default) |
| Build speed (40 pages) | 2–3 minutes | Under 10 seconds |
| SEO plugin ecosystem | Mature (gatsby-plugin-*) | Growing |
| Crawl-budget impact | Higher (more JS for Google to render) | Lower |
| Framework future | Uncertain (Netlify ownership, slowed activity) | Active, growing |
Both pre-render indexable HTML — that’s a wash. The difference is the JS tax: Astro’s islands ship a fraction of the JavaScript, which a Vaihe comparison frames as “Reduced JavaScript execution conserves crawl budgetThe number of URLs an engine will crawl in a timeframe. and accelerates page scanning.” (One historical caveat on the other side: Astro’s image handling lacked automatic width/height at the time of that comparison, which produced LighthouseLighthouse is Google's free, open-source tool that audits a page under simulated lab conditions and scores it 0–100 across Performance, Accessibility, Best Practices, and SEO. It's lab data — useful for debugging, not a ranking signal. warnings — check current Astro docs, as it may be resolved.)
For context on how the whole field compares, see the static site generatorsA build tool that pre-renders every page into static HTML files at deploy time, so the server delivers complete HTML without executing code per request — eliminating JavaScript rendering delays for search engine crawlers. hub.
The honest part: Gatsby’s maintenance trajectory
I won’t sugarcoat this, and I won’t catastrophize it either.
Netlify acquired Gatsby Inc. in February 2023. Gatsby Cloud was sunset and customers were moved to Netlify; Netlify stated the acquisition would “not impact Gatsby JS.” Since then, activity has slowed markedly. A widely-read community GitHub discussion (#39062) argues Gatsby is effectively abandoned — minimal commits, no React 19 support, a 2024 roadmap that wasn’t delivered, and the telemetry service shut down. Maintainers have framed the current state as security fixes, limited dependency updates, and low-hanging-fruit bug fixes.
What that means for SEO teams. For an existing Gatsby site, none of this is
an emergency — it builds, it indexes, it works. The risk is ecosystem decay over
time: SEO depends on plugins (gatsby-plugin-sitemap, image, canonical-urls), and
aging plugins (e.g. gatsby-source-shopify facing API deprecation) can eventually
break in ways that quietly degrade indexing. For a new project, weigh that
seriously — the frameworks people are migrating to are Astro and Next.js.
Production checklist: verify every rendering path
A local gatsby develop session or even a clean gatsby build log doesn’t prove
what a crawler actually receives. Because SSG, DSG, SSR, and client-only routes
each generate HTML differently, check production behavior per path rather than
assuming one representative page covers all of them:
- Raw HTML content. For each rendering path in use, fetch a real production
URL with
curl -s <url>orview-source:— not DevTools — and confirm the content, title, and meta tags a crawler would see are actually there. - Metadata (Head export output). Confirm the
Headexport’s tags land in that raw HTML for the path in question. This is where DSG/SSR differ most from SSG: the tags exist in your source either way, but only a production fetch proves they made it into the response. - HTTP status. Check the response status code on production, especially for SSR and DSG routes — a page that renders fine locally can 500 in production on first request or under load in ways a build never surfaces.
- Caching behavior. SSG output is a static file with predictable caching. DSG caches after the first request — confirm the second request is fast and correct, not just the first. SSR responses depend on your caching headers and hosting layer; verify stale or per-request content isn’t served to the wrong visitor.
- Failure and empty-state behavior. For SSR and DSG pages backed by request-time or first-request data, check what a crawler sees if that data fetch fails or returns empty — an unhandled error state is not the same page you tested locally with good data.
- Sitemap generation and exclusions. Re-confirm this only happens on a
production build (
gatsby build && gatsby serve, nevergatsby develop), and that the exclusions you expect — drafts, client-only routes, anything you decided shouldn’t be listed — are actually absent from the generatedsitemap-index.xml, not just absent from your intent.
None of this is optional per rendering path — a working gatsby build proves SSG
output, not DSG, SSR, or client-only behavior.
Common Gatsby SEO mistakes
- Still using
gatsby-plugin-react-helmet— legacy; migrate to the Head API. - Meta tags in DevTools but not in page source — the SEO component is rendering
client-side; check
view-source:, not DevTools. - Draft content in sitemaps — filter drafts in
gatsby-node.jswith GraphQL, not in a React component. - Orphan pagesAn orphan page is a page on your site that no other page links to internally. Because crawlers discover pages by following links, an orphan page is effectively invisible to search engines unless it's in an XML sitemap or linked from an external site. from
src/pages— Gatsby auto-routes everything there; stale files build and land in the sitemap. - Double canonical tags —
gatsby-plugin-canonical-urls+ react-helmet both firing. - Trailing-slash inconsistency —
<Link>client-routing bypasses server-side trailing-slash redirectsA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't.. - Not stripping query strings from canonicals — set
stripQueryString: true. - Alt text omitted on
GatsbyImage— it isn’t auto-generated. - Submitting
/sitemap.xml— the real file is/sitemap-index.xml. - Testing the sitemap in
gatsby develop— it only generates ongatsby build. - NoindexNoindex is a directive that tells search engines to keep a page out of their index, so it won't appear in search results. It works only on pages a crawler can actually fetch — a page blocked in robots.txt can never be noindexed. toggled by React state — Google may have already processed the raw
HTML; and when it sees
noindexin raw HTML it may skip rendering entirely. Keep noindex decisions in static HTML or server headers.
For the JavaScript-rendering fundamentals behind all of this, see the parent JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. hub.
AI summary
A condensed take on the Advanced version:
- Gatsby has four renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. paths — SSG, DSG, SSR, and client-only routes.
SSG is the default:
gatsby buildpre-renders the page to static HTML, so crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. get full content on the first fetch — no renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. queue, and the strongest indexability baseline. DSG defers generation to the first request; SSR generates per request; client-only routes render nothing route-specific until JavaScript runs in the browser (same profile as a plain client-side React app). Verify production HTML per path — a successfulgatsby buildonly proves SSG behavior. - The React-bundle cost is performance, not crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index., and applies regardless of rendering path. Gatsby hydrates a full React runtime (~200KB+) on the client, which hits Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data.. The zero-JS generators (Hugo, Jekyll, Eleventy) don’t have this; Astro ships ~5KB via islands.
- Use the Gatsby Head API (v4.19+), a named
Headexport from page/template files, for titles/meta/canonical/JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal.. It works across all four rendering paths and replaces the legacygatsby-plugin-react-helmet. It works only in pages/templates and dedupes byid. - Production checklist per path: raw HTML content, Head-export metadata, HTTP status, cachingCaching stores a copy of a page or resource — in a browser, a CDN edge node, or a search crawler's own cache — so it can be served again without regenerating or re-downloading it. It isn't a direct ranking factor, but it feeds page speed and crawl efficiency. behavior, and failure/empty-state handling — check all five for whichever rendering path a route actually uses, plus confirm sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. generation and exclusions only on a production build.
- SitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.:
gatsby-plugin-sitemapgeneratessitemap-index.xml(submit that, not/sitemap.xml) and only runs ongatsby build, not in dev. Google ignores<priority>/<changefreq>— focus on<lastmod>. Filter drafts ingatsby-node.js. DSG, SSR, and client-only routes each need an explicit sitemap-inclusion decision — don’t assume they’re covered the same way an SSG page is. - Canonicals: Head API +
location.pathname, orgatsby-plugin-canonical-urlswithstripQueryString: true— never both (double canonical bug). - Images:
gatsby-plugin-imageauto-handles sizes/WebP/lazy-load and prevents CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good., but does not write alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. — that’s manual. - The “DevTools but not view-source” bug = SEO component rendering client-side
instead of via the
Headexport. - Maintenance risk: Netlify acquired Gatsby in 2023; activity has slowed (no React 19, undelivered roadmap). Fine for existing sites; for new projects weigh Astro/Next.js.
Official documentation
Primary-source documentation from Gatsby and from Google.
Gatsby
- Rendering options — the overview of Gatsby’s four renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. paths: SSG, DSG, SSR, and client-only routes.
- Using Deferred Static Generation — DSG’s first-request generation behavior.
- Using Server-Side Rendering — SSR’s request-time output via Gatsby Functions.
- Client-only routes and user authentication — why client-only routes don’t expose route-specific content in the initial HTML.
- Adding an SEO component — the recommended SEO-component pattern.
- Gatsby Head API reference — the modern, built-in way to manage head tagsMeta tags are HTML elements in a page's head that pass metadata about the page to search engines and browsers. For SEO only a few matter — the title element, the meta description, and the robots meta tag — while meta keywords and most others are ignored..
- Introducing the Gatsby Head API — why it replaced react-helmet.
- gatsby-plugin-sitemap — sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. generation (production builds only).
- gatsby-plugin-image —
StaticImage/GatsbyImage, formats, CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good. prevention. - gatsby-plugin-robots-txt — environment-aware robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere..
- gatsby-plugin-canonical-urls — site-wide canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. and
stripQueryString. - gatsby-plugin-react-helmet — the legacy metadata approach.
- gatsby-plugin-next-seo — pre-built JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal./schema components.
- React hydration in Gatsby — how the static HTML becomes interactive.
- Understand the JavaScript SEO basics — the crawl → render → indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. phases, and why pre-renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. helps.
Quotes from the source
On-the-record statements that apply directly to Gatsby’s architecture. Gatsby is a JavaScript SSG, so the relevant primary sources are Google’s JavaScript-renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. guidance and Google reps on the same.
Google Search Central — JavaScript SEOMaking sure search engines can crawl, render, and index content that depends on JavaScript. basics
- “Server-side or pre-renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. is still a great idea because it makes your website faster for users and crawlersA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index., and not all bots can run JavaScript.” — the case for exactly what Gatsby does at build time.
- “The page may stay on this queue for a few seconds, but it can take longer than that.” — on the rendering queue that pre-rendered HTML lets you skip.
Martin Splitt, Google (Developer Advocate)
- “Even though GooglebotGooglebot is Google's web crawler — the software that fetches pages so Google can index and rank them. It comes in two variants, Googlebot Smartphone (primary, under mobile-first indexing) and Googlebot Desktop, and runs an evergreen Chromium renderer. can render JavaScript, we don’t want to rely on that.” — the principle behind preferring SSG/SSR. (Via SEJ / Botify coverage.)
- “A lot of people are still looking at view source. That is not what we use for indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed.. We use the rendered HTML.” — the exact reason the “DevTools vs view-source” Gatsby bug confuses people. (Via SEJ.)
- “The median time in the render queue is only five seconds.” — note this is the median; tail latency can be far longer. (BrightonSEO, via SEJ.)
John Mueller, Google (Search Advocate)
- “Server-side rendering is not a requirement there. We can render JavaScript-based pages for the most part.” — context: SSR/SSG is best practice, not strictly required; Google will eventually render CSR content. (Via SEJ.)
Gatsby SEO checklist
A pass to confirm a Gatsby build is actually search-friendly:
- Primary content appears in View Source (raw HTML), not just in DevTools — confirms it’s pre-rendered, not client-side only.
- Metadata is set via the Gatsby Head API (
Headexport from pages/ templates), not legacy react-helmet on new work. - Every page has a unique, build-time
<title>and meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through., with asiteMetadatafallback. - Canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. are set one way only — Head API or
gatsby-plugin-canonical-urls, never both (avoids the double-canonical bug). -
stripQueryString: trueis set so query-string variants don’t fragment canonicals. -
gatsby-plugin-sitemapis installed; you verified the output withgatsby build && gatsby serve(it doesn’t run in dev). - You submitted
/sitemap-index.xml(not/sitemap.xml) in Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results.. - Draft content is filtered out in
gatsby-node.js(GraphQL), so it never enters the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.. - No stale/orphan files in
src/pagesthat auto-build into routes. -
robots.txtblocks Netlify preview/branch deploys but leaves production open. - Every
GatsbyImage/StaticImagehas explicit alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search.. - JS/CSS assets aren’t blocked in
robots.txt. - Trailing-slash form is decided and enforced at the host/CDN (not just via
<Link>). -
noindexdecisions live in static HTML or server headers, not React state. - Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. checked — the React bundle is the likely drag; consider partial hydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM. where viable.
- For every renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. path in use (SSG, DSG, SSR, client-only), you checked
production raw HTML, metadata, HTTP status, cachingCaching stores a copy of a page or resource — in a browser, a CDN edge node, or a search crawler's own cache — so it can be served again without regenerating or re-downloading it. It isn't a direct ranking factor, but it feeds page speed and crawl efficiency. behavior, and
failure/empty-state handling — not just a local
gatsby buildlog. - DSG, SSR, and client-only routes each have an explicit, verified sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. decision — none of them are covered by the same assumption as an SSG page.
The mental models
1. CrawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index. vs performance are two separate scorecards. Gatsby aces crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index. (HTML is pre-rendered) but pays on performance (the React bundle). Don’t conflate them — “Gatsby is bad for SEO because React” mixes up the two. Your content indexes fine; your Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. are where the JS tax shows up.
2. The earlier the HTML exists, the less can go wrong. SSG decides HTML at build time — the safest point. Gatsby’s default page is an SSG, so it’s on the safe end of the renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. spectrum (client-only → SSR → DSG → SSG, roughly riskiest to safest for indexability). But Gatsby is a multi-mode framework — a given page might be DSG, SSR, or client-only instead, each earning its own place on that spectrum. Know which mode each route actually uses before assuming it’s on the safe end.
3. Head API by default; react-helmet only by inheritance.
For anything new, the decision is made: Gatsby Head API. react-helmet is a thing you
migrate off of, not something you reach for. If a tag isn’t showing in raw HTML,
the first question is “is this in a Head export, or stuck in a component?”
4. Pick exactly one canonical mechanism.
Head API canonical or gatsby-plugin-canonical-urls — never both. Two mechanisms =
two canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. = the double-canonical bug. Same discipline for anything that
writes to <head>: one source of truth per tag.
5. Gatsby builds everything it finds — so filtering is your job.
Drafts, stale src/pages files, query-string variants: Gatsby doesn’t editorialize.
If you don’t exclude it (in gatsby-node.js, in canonical config, in sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing.
options), it ships and gets crawled.
6. New project vs existing site changes the maintenance calculus. Existing Gatsby site: keep it, it works. New project: the slowed maintenance and ecosystem-decay risk are real inputs — weigh Astro/Next.js before committing.
Gatsby SEO — cheat sheet
Metadata: which approach
| Approach | Status | Use it when |
|---|---|---|
Gatsby Head API (Head export) | Current (v4.19+) | All new work; pages/templates only |
gatsby-plugin-react-helmet | Legacy | Existing sites pending migration |
Plugins at a glance
| Plugin | Does | SEO gotcha |
|---|---|---|
gatsby-plugin-sitemap | Generates sitemap-index.xml | Production build only; submit the indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. URL |
gatsby-plugin-image | Sizes, WebP/AVIF, lazy-load, CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good.-safe | No auto alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. |
gatsby-plugin-canonical-urls | Site-wide canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. | Don’t pair with react-helmet canonicals |
gatsby-plugin-robots-txt | Build-time robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. | Use env rules to block preview deploys |
gatsby-plugin-next-seo | Pre-built JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. components | — |
gatsby-plugin-manifest | PWA manifest (icons/theme) | Not structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding. |
Fast facts
- Sitemap fileA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. is
/sitemap-index.xml, not/sitemap.xml. - SitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. doesn’t generate in
gatsby develop— usegatsby build && gatsby serve. - Google ignores
<priority>/<changefreq>— accurate<lastmod>only. - React bundle ≈ 200KB+ per page (full hydrationTurning HTML, CSS, and JavaScript into the final visual page and DOM.); a CWV cost, not a crawl one.
- Astro ships ~5KB by comparison (islands only).
gatsby develop≠ production: sitemap, robots, and some optimizations differ.- Netlify acquired Gatsby Feb 2023; maintenance has slowed (no React 19 yet).
Two-line SEO Head export
export const Head = ({ data, location }) => (
<>
<title>{data.post.title}</title>
<link rel="canonical" href={`https://example.com${location.pathname}`} />
</>
) Check Gatsby’s production artifacts
Run this after gatsby build, not against gatsby develop:
find public -name '*.html' -type f | while IFS= read -r file; do
title_count=$(grep -Eio '<title>[^<]*</title>' "$file" | wc -l | tr -d ' ')
canonical_count=$(grep -Eio '<link[^>]+rel=["'"']canonical["'"'][^>]*>' "$file" | wc -l | tr -d ' ')
robots=$(grep -Eio '<meta[^>]+name=["'"']robots["'"'][^>]*>' "$file" | head -1)
if [ "$title_count" -ne 1 ] || [ "$canonical_count" -ne 1 ]; then
printf '%s\ttitles=%s\tcanonicals=%s\t%s\n' "$file" "$title_count" "$canonical_count" "$robots"
fi
doneThis catches missing or duplicate output from overlapping Head API/plugin mechanisms. Review the values and sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. membership separately.
Patrick's relevant free tools
- SEO Incident Simulator — Practice thirty deterministic technical SEO incident investigations — indexability, crawl controls, redirects, sitemaps, markup, caching, DNS, bot verification, rendering, hreflang, and faceted navigation — with clearly labeled fixture evidence and Find → Fix → Verify handoffs.
- Raw vs. Rendered HTML Checker — See what's in your page's initial HTML versus after JavaScript runs — headless-Chrome rendering only when the page actually needs it, a rendering-strategy verdict (SSR / prerendered / CSR / hybrid), ~15 calibrated JavaScript-SEO checks (noindex, canonicals, robots.txt blocking, links, soft 404s), a side-by-side raw-vs-rendered diff, and shareable reports.
Tools for Gatsby SEO
- Gatsby Head API (built-in, v4.19+) — the no-dependency way to manage
<title>, meta, canonical, and JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal. per page/template. gatsby-plugin-sitemap— generatessitemap-index.xmlon production builds.gatsby-plugin-image—StaticImage/GatsbyImage, responsive sizes, WebP/AVIF, lazy loadingLazy loading defers loading of off-screen or non-critical resources — usually images and iframes — until they're about to enter the viewport. The native way to do it is the loading=\"lazy\" HTML attribute, which needs no JavaScript., CLSCumulative Layout Shift — a unitless score for unexpected visual movement, taken from the largest burst (session window) of layout shifts, not the lifetime sum. ≤0.1 is good.-preventing placeholders.gatsby-plugin-canonical-urls— site-wide canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. withstripQueryString.gatsby-plugin-robots-txt— environment-aware robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere. (block preview deploys).gatsby-plugin-next-seo— pre-built JSON-LDJSON-LD (JavaScript Object Notation for Linked Data) is a script-based structured data format, typically paired with the schema.org vocabulary to describe page content for search engines and AI systems. Google recommends it over Microdata and RDFa because it's the easiest format to implement and maintain at scale — but all three work, and structured data isn't a ranking signal./schema components if you don’t want to hand-roll structured dataStructured data is a standardized way of labeling page content (using the schema.org vocabulary in JSON-LD, Microdata, or RDFa) so search engines can understand its meaning. It's not a direct ranking factor — its value is rich results and entity understanding..view-source:/ GSCA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results. URL InspectionA Google Search Console feature that reports how Google sees one specific URL on a property you own. By default it shows the last-indexed snapshot; a separate \"Test live URL\" mode fetches the current version. — the ground truth for “is my content and metadata in the raw HTML?” (not DevTools, which shows the hydrated DOM).- LighthouseLighthouse is Google's free, open-source tool that audits a page under simulated lab conditions and scores it 0–100 across Performance, Accessibility, Best Practices, and SEO. It's lab data — useful for debugging, not a ranking signal. / PageSpeed InsightsPageSpeed Insights (PSI) is a free Google tool at pagespeed.web.dev that reports two kinds of data for a URL: real-user field data from the Chrome UX Report and a single Lighthouse lab run with the 0–100 Performance score. Only the field Core Web Vitals are what Google uses for ranking. — to see the Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. cost of the React bundle and whether partial hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. is worth pursuing.
gatsby build && gatsby serve— the only way to test sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere., and production-only optimizations locally.
What not to do on a Gatsby site
Concrete mistakes people actually make building and shipping Gatsby sites — prevention, not diagnosis.
Keeping gatsby-plugin-react-helmet on new work
The mistake: reaching for react-helmet by habit on new pages or a new project, because that’s what older Gatsby tutorials still show.
Why it’s wrong: react-helmet has known issues with React Hooks and concurrent renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., plus a background-tab title bug you have to patch with defer={false}. It’s also a third-party bundle and Provider wrapper you don’t need anymore.
What to do instead: use the built-in Gatsby Head API (v4.19+) — a named Head export from the page or template file. Reserve react-helmet for existing code you haven’t migrated yet.
Running two canonical mechanisms at once
The mistake: setting a canonical via gatsby-plugin-canonical-urls and a react-helmet (or Head API) canonical tagA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. on the same page.
Why it’s wrong: both fire, and the page ships two <link rel="canonical"> tags — a well-known Gatsby failure mode that muddies which URL you actually intended as canonical.
What to do instead: pick exactly one mechanism site-wide (the Head API, or the plugin with stripQueryString: true) and drop the other entirely.
Filtering drafts in a React component instead of at build time
The mistake: hiding unpublished content with a client-side check (e.g. if (!post.published) return null) and assuming that keeps it out of search.
Why it’s wrong: Gatsby builds everything it finds into static HTML before that component ever runs in the browser — the draft page is already built and already listed in the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. by the time your React check executes.
What to do instead: filter drafts in gatsby-node.js with a GraphQL query (e.g. excluding entries without a publish date) so they never get built or listed in the first place.
Shipping GatsbyImage without alt text
The mistake: trusting gatsby-plugin-image to handle image SEOImage SEO is optimizing the images on your pages so search engines can discover, crawl, index, and rank them — in Google Images and visual search, and as part of standard web results. It spans file format, filenames, alt text, compression, responsive markup, structured data, and image sitemaps. end-to-end because it auto-generates sizes, formats, and placeholders.
Why it’s wrong: the plugin optimizes delivery — it does not write alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search.. Missing alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search. on GatsbyImage/StaticImage is one of the most common Gatsby SEOGatsby SEO is the set of practices for getting a Gatsby site found and ranked. By default, Gatsby pre-renders HTML at build time (SSG), putting content in the raw HTML on the first fetch — but Gatsby also supports DSG, SSR, and client-only routes that don't all work that way, and it ships a full React bundle, so canonical tags, sitemaps, and Core Web Vitals still need deliberate work. oversights precisely because everything else about the image looks handled.
What to do instead: treat alt text as a required field on every image component, every time — it’s the one piece of image SEOImage SEO is optimizing the images on your pages so search engines can discover, crawl, index, and rank them — in Google Images and visual search, and as part of standard web results. It spans file format, filenames, alt text, compression, responsive markup, structured data, and image sitemaps. Gatsby leaves entirely to you.
Assuming gatsby develop shows you production SEO output
The mistake: checking the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere., or noindexNoindex is a directive that tells search engines to keep a page out of their index, so it won't appear in search results. It works only on pages a crawler can actually fetch — a page blocked in robots.txt can never be noindexed. behavior in gatsby develop and concluding it’s broken because nothing shows up.
Why it’s wrong: gatsby-plugin-sitemap and several other build-time behaviors simply don’t run in development mode — you’re not looking at a bug, you’re looking at dev mode doing what dev mode does.
What to do instead: run gatsby build && gatsby serve before drawing any conclusion about sitemap, robots.txtA plain-text file at the root of a host that tells crawlers which URLs they may and may not request. It controls crawling, not indexing — a blocked URL can still be indexed if it's linked from elsewhere., or production-only optimizations.
Letting <Link> client-routing hide trailing-slash inconsistency
The mistake: assuming your server-side trailing-slash redirectA redirect sends browsers and crawlers from a requested URL to a different one. An HTTP redirect specifically is a 3xx status code paired with a Location header; meta refresh and JavaScript redirects achieve a similar navigation without being a 3xx response themselves. Permanent redirects (301/308) are Google's signal the target should be canonical; temporary ones (302/303/307) aren't. rules apply everywhere, including in-app navigation.
Why it’s wrong: Gatsby’s <Link> component uses client-side History API routing, which bypasses the server-side 301 redirectsA 301 redirect is the HTTP status code for a permanent move: it tells browsers and search engines a URL has moved for good, and it's the strongest signal for consolidating a page's ranking signals onto the new URL. Google says permanent redirects don't cause a loss in PageRank. you’d normally rely on to normalize trailing slashes — so internal linksAn internal link is a hyperlink from one page on a website to another page on the same website. Internal links help search engines discover your pages and pass ranking signals (PageRank and anchor-text context) between them. can quietly serve the “wrong” form without ever hitting your redirect rule.
What to do instead: decide on one trailing-slash form, enforce it at the host/CDN level, and make sure canonicals are consistent with it regardless of how a given page was reached.
Common Gatsby SEO issues
Symptom-first lookup for problems you’re actively seeing on a Gatsby site — start from what you observe.
Meta tags show up in DevTools but are missing from view-source
Symptom: you inspect the page in Chrome DevTools and the title/meta descriptionThe meta description is an HTML head tag — `<meta name=\"description\" content=\"…\">` — that suggests a short summary of the page for the search snippet. It's not a Google ranking factor, and Google rewrites it the majority of the time, but a good one can still lift click-through. look correct, but view-source: (or a raw curl) shows them missing or generic.
Likely cause: the SEO component is renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. client-side — it’s used as a regular component rather than as (or inside) the page’s Head export, so it only appears in the DOM after hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers..
Fix: move the tags into the page or template’s named Head export. Confirm with view-source: or curl -s <url> — not DevTools, which shows the hydrated DOM, not what a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index.’s raw fetch sees.
The sitemap is missing or empty
Symptom: you visit /sitemap-index.xml (or check Search ConsoleA free Google service that reports how a site performs in Google Search and surfaces problems with how Google crawls, indexes, and serves it. It's first-party data straight from Google — but you don't need it to appear in results.) and get nothing, or an incomplete list.
Likely cause: almost always that you tested in gatsby develop, where gatsby-plugin-sitemap doesn’t run at all. Less commonly, the plugin isn’t installed/configured in gatsby-config.js.
Fix: run gatsby build && gatsby serve and check again. If it’s still missing, verify the plugin is present in gatsby-config.js. Confirm by requesting /sitemap-index.xml directly — remember it’s the indexStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed. URL, not /sitemap.xml.
A page has two canonical tags
Symptom: viewing source (or running a schema/tag audit) shows two <link rel="canonical"> elements on the same page.
Likely cause: gatsby-plugin-canonical-urls and a react-helmet (or Head API) canonical are both firing for the same page.
Fix: remove one mechanism so only a single source sets the canonical. Confirm by viewing source again and checking with Patrick’s Canonical Tag Checker that exactly one canonical resolves.
Draft or orphan pages appear in the sitemap
Symptom: the sitemapA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. (or Search ConsoleGoogle's free tool for monitoring crawling, indexing, and search performance. coverage) lists URLs you never meant to publish — draft posts, or stale files under src/pages.
Likely cause: Gatsby auto-builds and auto-routes everything it finds — an unpublished GraphQL entry that isn’t filtered, or a leftover file in src/pages, gets built into a real static page and listed like any other.
Fix: filter drafts in gatsby-node.js with a GraphQL query (e.g. excluding entries without a publish date) so they’re never built. For orphan src/pages files, delete the stale file — filtering in a component is too late, since the page is already built by then.
Query-string variants of a page are getting indexed as duplicates
Symptom: Search Console shows near-duplicateThe same or very similar primary content reachable at more than one URL. There's no general duplicate content penalty — the real costs are possible signal dilution, the wrong URL getting chosen, and less-efficient crawling. URLs like /blog and /blog?tag=foo both indexed, or flagged as duplicate contentThe same or very similar primary content reachable at more than one URL. There's no general duplicate content penalty — the real costs are possible signal dilution, the wrong URL getting chosen, and less-efficient crawling..
Likely cause: gatsby-plugin-canonical-urls is running without stripQueryString: true, so query-string variants are canonicalized to themselves instead of the clean URL.
Fix: set stripQueryString: true in the plugin config, rebuild, and re-check the canonical tagA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content. on a query-string URL — it should now point to the clean path.
A page you intended to noindex is still showing up in search
Symptom: you set a page to noindexNoindex is a directive that tells search engines to keep a page out of their index, so it won't appear in search results. It works only on pages a crawler can actually fetch — a page blocked in robots.txt can never be noindexed., but it’s still indexed weeks later, or Search Console shows it as indexed despite the tag.
Likely cause: the noindex directive was toggled via React state rather than baked into static HTML or server headers — Google may have already processed the raw HTML (without the noindex) and, when it does see noindex only in a client-rendered pass, can skip renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. the page again entirely.
Fix: move the noindex decision into static HTML (via the Head export at build time) or an HTTP header, not conditional React logic. Recheck with view-source: to confirm the tag is present in the raw response.
Test yourself: Gatsby SEO
Five quick questions on optimizing a Gatsby site for search. Pick an answer for each, then check.
Resources worth your time
My related writing
- JavaScript SEO: A Definitive Guide — renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., DOM parityWhether the rendered DOM matches what you expect the raw HTML to become., and why static/prerendered output (like Gatsby’s) is the low-risk end of the spectrum.
- The Beginner’s Guide to Technical SEO — where renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. architecture fits in the bigger picture.
My speaking
- How Search Works (SlideShare) — my walkthrough of crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., rendering, indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and ranking. (My standing disclaimer applies: “This is my understanding of systems… not going to be 100% complete or accurate.”)
From around the industry
- Gatsby Head API reference — the primary source for the modern metadata approach.
- Introducing the Gatsby Head API — Gatsby’s own explanation of why it replaced react-helmet.
- Google Search Central — JavaScript SEO basics — the crawl → render → index phases Gatsby’s build lets you skip for content.
- Gatsby is joining Netlify — the 2023 acquisition announcement, for context on maintenance.
- Netlify acquires frontend platform Gatsby (TechCrunch) — independent coverage of the acquisition.
- “Is GatsbyJS abandoned?” discussion #39062 — the community thread on Gatsby’s current maintenance state.
- SEO comparison of Gatsby vs Next vs Astro (Vaihe) — the JS-payload and crawl-budget comparison.
- Understanding partial hydration in Gatsby 5 (LogRocket) — the CWVGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data.-relevant hydrationActivating server-rendered HTML in the browser by attaching JavaScript handlers. improvement and its limits.
Gatsby SEO
Gatsby SEO is the set of practices for getting a Gatsby site found and ranked. By default, Gatsby pre-renders HTML at build time (SSG), putting content in the raw HTML on the first fetch — but Gatsby also supports DSG, SSR, and client-only routes that don't all work that way, and it ships a full React bundle, so canonical tags, sitemaps, and Core Web Vitals still need deliberate work.
Related: Static Site Generator, JavaScript SEO
Gatsby SEO
Gatsby SEO covers the technical and on-page work involved in optimizing a Gatsby site — a React-based framework — for crawlingCrawling is how search engines use automated bots (like Googlebot and Bingbot) to discover URLs and download pages. A page has to be crawlable to be indexed, but crawling on its own isn't a ranking factor., indexingStoring a crawled page in the search index so it can appear in results. Crawled is not the same as indexed — Google selects what to keep, and indexing isn't guaranteed., and performance. Gatsby supports four renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM. paths per page or template: SSG (static site generation, the default), DSG (deferred static generation, generated on first request), SSR (server-side renderingTurning HTML, CSS, and JavaScript into the final visual page and DOM., generated per request), and client-only routes (rendered entirely in the browser). SSG’s defining SEO trait is that it pre-renders to static HTML at build time (gatsby build), so a crawlerA crawler — also called a spider or bot — is an automated program that fetches web pages, extracts their links, and queues new URLs to visit. Search engines use crawlers to discover and download content for their index. gets fully rendered content on the first fetch with no rendering queue to wait on — a strong baseline compared to a pure client-side React app, which serves an empty shell. DSG, SSR, and client-only routes don’t share that guarantee and each need their own production check.
The catch is that Gatsby still ships the full React runtime (~200KB+) to the browser and hydrates the static HTML to add interactivity. That doesn’t hurt crawlabilityCrawlability is how well search engine crawlers can discover, access, and fetch a site's pages. A crawlability issue is any technical condition — blocked access, broken links, server failures, or bloated URL inventory — that stops pages from reaching the index. — the HTML is already complete — but it does carry a Core Web VitalsGoogle's three real-user UX metrics — LCP (loading), INP (responsiveness), and CLS (visual stability) — used by Google's ranking systems, with no official weight attached, measured on field data. cost the zero-JS generators (Hugo, Jekyll, Eleventy) don’t have.
The current way to manage page metadata is the Gatsby Head API (v4.19+): a named Head export from each page or template that replaces the legacy gatsby-plugin-react-helmet. SitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing. come from gatsby-plugin-sitemap (production builds only), images from gatsby-plugin-image, and canonicals from the Head API or gatsby-plugin-canonical-urls. The common pitfalls are double canonical tagsA rel=\"canonical\" annotation — in the HTML <head> or an HTTP Link header — that tells search engines which URL is the preferred version of duplicate or near-duplicate content., draft and orphan pagesAn orphan page is a page on your site that no other page links to internally. Because crawlers discover pages by following links, an orphan page is effectively invisible to search engines unless it's in an XML sitemap or linked from an external site. leaking into sitemapsA sitemap is a file that lists the pages, images, videos, and other files on your site so search engines can discover them. It helps discovery, but submitting a sitemap doesn't guarantee crawling or indexing., missing alt textAlt text is the value of the `alt` attribute on an HTML `<img>` element — a short text substitute chosen for the image's purpose and context, not a literal description of what it shows. It makes images accessible to screen-reader users and helps search engines understand images, mainly for image search., and a framework whose maintenance has slowed sharply since Netlify’s 2023 acquisition.
Related: Static Site Generator, JavaScript SEO
Build-time retrieval analysis plus live signals for this exact article. The automatic chunk report includes a deterministic readiness score and is ready without a model download.
Search Console
sampleGA4 traffic (28d)
sampleCloudflare traffic (7d)
sampledCrUX field data (28d, phone)
sampleGoogle NLP entities
localChangelog
Revision history
Compare the published article with an archived editorial snapshot. Added and removed words are shown only after you open a comparison.
Updated Jul 18, 2026.
Editorial summary and recorded change details.Summary
Corrected the article's universal build-time-HTML framing: Gatsby actually has four rendering paths — SSG, DSG, SSR, and client-only routes — and only SSG guarantees pre-rendered HTML at build time.
Change details
-
Added a rendering-options breakdown (SSG default, DSG first-request generation, SSR request-time rendering, client-only browser-only routes) with the SEO implications and production checks each one needs.
-
Added a Production checklist section covering raw HTML, Head-export metadata, HTTP status, caching, and failure/empty-state behavior to verify per rendering path.
-
Added sitemap guidance for DSG, SSR, and client-only routes, which don't get the same automatic sitemap treatment as SSG pages.