Gatsby Guide — React-Based Static Site Generator
Gatsby is a React-based static site generator that combines GraphQL data sourcing, powerful plugin architecture, and optimized build pipeline to create blazing-fast websites with excellent SEO, progressive image loading, and automatic performance optimization.
What You’ll Learn
You’ll understand Gatsby’s architecture, source and query data with GraphQL, create pages programmatically and with file-based routing, extend functionality with plugins and themes, optimize images with gatsby-plugin-image, and deploy with Gatsby Cloud.
Why Gatsby Matters
Gatsby pioneered the Jamstack architecture — pre-building HTML pages at compile time instead of rendering them on every request. This means your site loads instantly from CDN edge nodes with no server round-trip. Google’s research shows that sites loading in under one second have 3x higher conversion rates than those taking three seconds. DodaTech’s knowledge base uses Gatsby because its combination of React interactivity and static performance delivers both rich UX and instant load times.
Gatsby Architecture
flowchart LR
A[Data Sources] --> B[Gatsby Build]
B --> C[GraphQL Data Layer]
C --> D[React Components]
D --> E[Static HTML Pages]
E --> F[CDN Deployment]
G[Plugins] --> B
H[Themes] --> B
I[gatsby-config] --> B
B:::current
classDef current fill:#663399,color:#fff,stroke:#333,stroke-width:2px
Project Structure
A typical Gatsby project follows this structure:
my-gatsby-site/
├── src/
│ ├── pages/ # File-based pages
│ │ ├── index.js
│ │ └── about.js
│ ├── components/ # Reusable components
│ ├── templates/ # Page templates for programmatic pages
│ └── images/ # Imported images
├── static/ # Raw static assets
├── gatsby-config.js # Site configuration and plugins
├── gatsby-node.js # Node API for programmatic page creation
└── gatsby-browser.js # Browser API hooksData Layer with GraphQL
Gatsby sources data from multiple origins (filesystem, CMS, APIs) and unifies them into a single GraphQL layer:
// gatsby-config.js
module.exports = {
siteMetadata: {
title: 'DodaTech Tutorials',
description: 'Learn programming and security',
siteUrl: 'https://dodatech.com'
},
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'content',
path: `${__dirname}/content/`
}
},
'gatsby-transformer-remark', // Transforms .md files to HTML
'gatsby-plugin-image',
'gatsby-plugin-sharp',
'gatsby-transformer-sharp'
]
};Query data in any component using Gatsby’s useStaticQuery hook or page query:
import { graphql, useStaticQuery } from 'gatsby';
import { GatsbyImage } from 'gatsby-plugin-image';
function HeroSection() {
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
description
}
}
heroImage: file(relativePath: { eq: "hero.jpg" }) {
childImageSharp {
gatsbyImageData(
width: 1200
placeholder: BLURRED
formats: [WEBP, AVIF]
)
}
}
}
`);
return (
<section>
<h1>{data.site.siteMetadata.title}</h1>
<p>{data.site.siteMetadata.description}</p>
<GatsbyImage
image={data.heroImage.childImageSharp.gatsbyImageData}
alt="Hero banner"
loading="lazy"
/>
</section>
);
}Output: At build time, Gatsby fetches the site metadata and hero image from GraphQL, generates an optimized WebP/AVIF image with a blurred placeholder, and renders the hero section with the pre-built HTML. No client-side data fetching is needed.
File-Based Pages and Programmatic Pages
File-Based Pages
Pages in src/pages/ become routes automatically:
// src/pages/about.js
import React from 'react';
import { graphql } from 'gatsby';
export default function AboutPage({ data }) {
return (
<main>
<h1>{data.markdownRemark.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }} />
</main>
);
}
// Page query — runs at build time
export const query = graphql`
query ($id: String) {
markdownRemark(id: { eq: $id }) {
frontmatter { title }
html
}
}
`;Programmatic Pages with gatsby-node.js
For dynamic content like blog posts, create pages programmatically:
// gatsby-node.js
const path = require('path');
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
// Query all blog posts
const result = await graphql(`
query {
allMarkdownRemark(filter: { frontmatter: { type: { eq: "post" } } }) {
nodes {
id
frontmatter {
slug
title
}
}
}
}
`);
// Create a page for each post
result.data.allMarkdownRemark.nodes.forEach((node) => {
createPage({
path: `/blog/${node.frontmatter.slug}/`,
component: path.resolve('./src/templates/blog-post.js'),
context: {
id: node.id
}
});
});
};// src/templates/blog-post.js
import React from 'react';
import { graphql } from 'gatsby';
export default function BlogPost({ data }) {
const post = data.markdownRemark;
return (
<article>
<h1>{post.frontmatter.title}</h1>
<time>{post.frontmatter.date}</time>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
);
}
export const query = graphql`
query ($id: String) {
markdownRemark(id: { eq: $id }) {
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
}
html
}
}
`;Output: During gatsby build, the script queries all markdown posts, creates a unique URL for each one (e.g., /blog/hello-world/), and generates a static HTML file for each post. The GraphQL query in the template fetches the specific post’s data using the id passed from context.
Image Optimization with gatsby-plugin-image
Gatsby’s image plugin generates multiple sizes and formats automatically:
import { GatsbyImage, getImage } from 'gatsby-plugin-image';
function ProductCard({ product }) {
const image = getImage(product.image);
return (
<div className="product-card">
<GatsbyImage
image={image}
alt={product.name}
loading="lazy"
className="product-image"
/>
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
);
}Output: The plugin generates WebP, AVIF, and JPEG versions of the image at multiple widths (320px, 640px, 960px, 1280px). The browser downloads the smallest appropriate format. A blurred placeholder shows while loading. The image lazy-loads below the fold. All of this happens automatically — no manual image handling is needed.
Plugins and Themes
Source Plugins
Source plugins pull data from external services:
// gatsby-config.js
plugins: [
{
resolve: 'gatsby-source-contentful',
options: {
spaceId: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN
}
},
{
resolve: 'gatsby-source-wordpress',
options: {
url: 'https://example.com/graphql'
}
},
'gatsby-source-strapi'
]Transformer Plugins
Transformer plugins convert source data into queryable nodes:
// Example: Markdown files become queryable HTML nodes
// gatsby-transformer-remark + gatsby-source-filesystem
// Enables this query:
// query { allMarkdownRemark { nodes { html frontmatter { title } } } }
Themes
Themes bundle config, components, and data sourcing into reusable packages:
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: 'gatsby-theme-blog',
options: {
basePath: '/blog',
contentPath: 'content/posts'
}
},
{
resolve: 'gatsby-theme-docs',
options: {
basePath: '/docs'
}
}
]
};Output: Themes provide pre-built functionality (blog layouts, documentation navigation) that you can customize through shadowing — placing a file in the same path as the theme’s file overrides it. This gives you production-ready features with minimal code.
Building and Deploying
# Development
gatsby develop
# Starts dev server at localhost:8000 with hot reload
# Production build
gatsby build
# Output: public/ — static HTML, JS, CSS, images
# Serve built site locally
gatsby serve
# Starts production build at localhost:9000Deployment to Gatsby Cloud
Gatsby Cloud provides automatic builds, incremental builds, and preview deployments:
1. Connect your Git repository to Gatsby Cloud
2. Configure build environment variables
3. Enable Content Sync for CMS previews
4. Set up branch-based preview deployments
5. Deploy to the built-in CDN or connect a custom domainSecurity Angle: Build-Time Data Validation
// gatsby-node.js — Validate all Markdown pages at build time
exports.createPages = async ({ graphql, actions }) => {
const result = await graphql(`
query {
allMarkdownRemark {
nodes {
frontmatter {
title
slug
date
author
}
fileAbsolutePath
}
}
}
`);
const errors = [];
result.data.allMarkdownRemark.nodes.forEach((node) => {
if (!node.frontmatter.title) {
errors.push(`Missing title in ${node.fileAbsolutePath}`);
}
if (!node.frontmatter.slug) {
errors.push(`Missing slug in ${node.fileAbsolutePath}`);
}
if (node.frontmatter.slug && node.frontmatter.slug.includes(' ')) {
errors.push(`Slug contains spaces in ${node.fileAbsolutePath}`);
}
});
if (errors.length > 0) {
console.error('Build errors found:');
errors.forEach(e => console.error(` - ${e}`));
process.exit(1); // Fail the build
}
// Continue with page creation
};This pattern catches content errors before deployment, preventing broken pages on the live site. DodaTech’s documentation pipeline uses similar validation to ensure every tutorial has the required frontmatter fields before publishing.
Common Mistakes Beginners Make
Forgetting to restart
gatsby developafter config changes:gatsby-config.jsandgatsby-node.jschanges require restarting the dev server. Save-and-reload doesn’t pick them up.Querying non-existent fields: GraphQL queries that request fields not present in the schema fail the build. Always check the GraphiQL explorer at
localhost:8000/___graphqlfor available fields.Missing alt text on GatsbyImage: The
altprop is required. Forgetting it causes accessibility violations and may fail build-time checks.Using client-side data fetching unnecessarily: Gatsby’s strength is build-time data sourcing. Using
useEffect+fetchto load data that could come from GraphQL defeats the purpose of static generation.Ignoring the
public/directory size: Large images and excessive pages can makepublic/gigabytes in size. Audit generated HTML and optimize aggressively.Not configuring path prefix for subdirectory deployment: Deploying to
https://example.com/blog/requirespathPrefix: '/blog'ingatsby-config.js.
Practice Questions
- What is the difference between a page query and useStaticQuery?
- How do you create pages dynamically from Markdown files?
- What does gatsby-plugin-image do automatically?
- What happens if a GraphQL query fails during build?
- Why might you choose Gatsby over a plain React app?
Answers:
- Page queries are available only in top-level page components (
src/pages/and templates) and support variables.useStaticQueryworks in any component but doesn’t support variables. - Use
gatsby-node.js’screatePagesAPI to query Markdown files, iterate over them, and callcreatePagefor each one with a template component and context. - It generates multiple sizes, formats (WebP, AVIF), blurred placeholders, lazy loading, and responsive srcsets automatically.
- The build fails with an error message showing the query and the missing field. This prevents deploying broken pages.
- Gatsby pre-builds HTML at compile time, resulting in instant page loads from CDN, better SEO (full HTML on first response), and lower server costs.
Challenge
Build a documentation site with Gatsby: source content from Markdown files, create pages for each document using gatsby-node.js, add a sidebar navigation component that queries the page structure, enable search with client-side indexing, and deploy with Gatsby Cloud.
Real-World Task
Create a portfolio site that sources project data from Markdown files, uses gatsby-plugin-image for project screenshots, includes a tag-based filtering system, and implements a contact form that posts to a serverless function.
FAQ
Try It Yourself
# Create a new Gatsby site
npm init gatsby
cd my-gatsby-site
# Select "Add Markdown Blog" from the starter wizard
gatsby developAdd a new Markdown file to content/posts/ with frontmatter including title, date, slug, and tags. Create a tag archive page that lists posts grouped by tag using a page query.
What’s Next
Related topics: React, GraphQL, JavaScript, Node.js
What’s Next
Congratulations on completing this Gatsby tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro