Gatsby
Mature React + GraphQL based SSG. Rich plugin ecosystem with over 55k GitHub stars.
トレンド・動向
Established position as leader in Jamstack movement. Continuously adopted for marketing sites and blogs.
# Static Site Generator
Gatsby
## Overview
Gatsby is "a mature React + GraphQL based SSG" developed as a leader in the Jamstack movement with an established position in the ecosystem. Boasting over 55k GitHub stars, it features a rich plugin ecosystem and automatic performance optimization, being continuously adopted for marketing sites and blog construction. With a component-based development experience familiar to React developers and unified data management through GraphQL, it streamlines enterprise-level website construction.
## Details
Gatsby 5.x maintains its solid position as a React SSG with rich experience accumulated since its first release in 2015. The biggest feature is automatic performance optimization including automatic code splitting, image optimization, critical style inlining, lazy loading, and resource prefetching. The GraphQL data layer enables unified management of diverse data sources such as Markdown, CMS, and APIs. Through Slices API, partial hydration (Beta), DSG (Deferred Static Generation), and SSR, flexible rendering choices on a per-page basis achieve both performance and productivity.
### Key Features
- **Automatic Performance Optimization**: Automatic execution of code splitting, image optimization, and prefetching
- **GraphQL Data Layer**: Unified query interface for multiple data source management
- **Rich Plugin Ecosystem**: 3000+ official and community plugins
- **React Component-Based**: Full utilization of existing React knowledge
- **Multiple Rendering Strategies**: Per-page selection of SSG, DSG, and SSR
- **CDN Hosting Support**: Serverless low-cost operation possible
## Pros and Cons
### Pros
- Overwhelming maturity and rich learning resources in React ecosystem
- High-speed site generation and operational efficiency through automatic performance optimization
- Powerful and flexible data management and integration through GraphQL
- Extensibility and rich functionality through 3000+ plugins
- Excellent integration with major CMS like WordPress, Contentful, and Strapi
- Optimized deployment experience with Netlify, Vercel, and Gatsby Cloud
### Cons
- Declining development activity since 2024 and effective maintenance suspension
- Increased build time and memory usage for large-scale sites
- GraphQL learning cost and excessive complexity for small sites
- Delayed feature additions compared to competing frameworks like Next.js
- Compatibility issues due to plugin dependencies and complex version management
- Concerns about future prospects and adoption risks for new projects
## Reference Pages
- [Gatsby Official Site](https://www.gatsbyjs.com/)
- [Gatsby Documentation](https://www.gatsbyjs.com/docs/)
- [Gatsby GitHub Repository](https://github.com/gatsbyjs/gatsby)
## Code Examples
### Installation and Project Creation
```bash
# Install Gatsby CLI
npm install -g gatsby-cli
# Create new project
gatsby new my-gatsby-site
cd my-gatsby-site
# Start development server
gatsby develop
# Production build
gatsby build
# Preview build results
gatsby serve
```
### Basic Page Components
```jsx
// src/pages/index.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
const IndexPage = ({ data }) => {
const { site } = data
return (
<Layout>
<SEO title="Home" />
<div>
<h1>Welcome to {site.siteMetadata.title}</h1>
<p>A website built with the latest technology.</p>
<section>
<h2>Our Features</h2>
<ul>
<li>Fast page loading</li>
<li>SEO optimized</li>
<li>Mobile friendly</li>
</ul>
</section>
</div>
</Layout>
)
}
export default IndexPage
// Get site metadata with page query
export const query = graphql`
query {
site {
siteMetadata {
title
description
author
}
}
}
`
```
### Layout Components and StaticQuery
```jsx
// src/components/layout.js
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Header from "./header"
import Footer from "./footer"
import "./layout.css"
const Layout = ({ children }) => {
const data = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
description
social {
twitter
github
}
}
}
}
`)
const { title, description, social } = data.site.siteMetadata
return (
<>
<Header siteTitle={title} />
<div
style={{
margin: `0 auto`,
maxWidth: 960,
padding: `0 1.0875rem 1.45rem`,
}}
>
<main>{children}</main>
</div>
<Footer
description={description}
social={social}
/>
</>
)
}
export default Layout
```
### GraphQL Queries and Data Fetching
```jsx
// src/pages/blog.js
import React from "react"
import { graphql, Link } from "gatsby"
import Layout from "../components/layout"
import SEO from "../components/seo"
const BlogPage = ({ data }) => {
const posts = data.allMarkdownRemark.edges
return (
<Layout>
<SEO title="Blog" />
<div>
<h1>Blog Posts</h1>
{posts.map(({ node }) => {
const title = node.frontmatter.title || node.fields.slug
return (
<article key={node.fields.slug}>
<header>
<h3>
<Link to={node.fields.slug}>
{title}
</Link>
</h3>
<small>{node.frontmatter.date}</small>
</header>
<section>
<p
dangerouslySetInnerHTML={{
__html: node.frontmatter.description || node.excerpt,
}}
/>
</section>
</article>
)
})}
</div>
</Layout>
)
}
export default BlogPage
export const query = graphql`
query {
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: DESC }
) {
edges {
node {
excerpt
fields {
slug
}
frontmatter {
date(formatString: "MMMM DD, YYYY")
title
description
tags
}
}
}
}
}
`
```
### Plugin Configuration and gatsby-config.js
```javascript
// gatsby-config.js
module.exports = {
siteMetadata: {
title: `My Gatsby Site`,
description: `Fast website built with Gatsby`,
author: `@example`,
siteUrl: `https://example.com`,
social: {
twitter: `example`,
github: `example`,
},
},
plugins: [
`gatsby-plugin-react-helmet`,
`gatsby-plugin-image`,
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `blog`,
path: `${__dirname}/content/blog`,
},
},
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-images`,
options: {
maxWidth: 630,
},
},
`gatsby-remark-prismjs`,
`gatsby-remark-copy-linked-files`,
`gatsby-remark-smartypants`,
],
},
},
{
resolve: `gatsby-plugin-google-analytics`,
options: {
trackingId: `ADD YOUR TRACKING ID HERE`,
},
},
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `My Gatsby Site`,
short_name: `GatsbySite`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`,
},
},
`gatsby-plugin-offline`,
],
}
```
### Dynamic Page Generation (gatsby-node.js)
```javascript
// gatsby-node.js
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
// Add slug field
exports.onCreateNode = ({ node, getNode, actions }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark`) {
const slug = createFilePath({ node, getNode, basePath: `pages` })
createNodeField({
node,
name: `slug`,
value: slug,
})
}
}
// Create dynamic pages
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
// Blog post template
const blogPostTemplate = path.resolve(`./src/templates/blog-post.js`)
// Get all Markdown files
const result = await graphql(`
{
allMarkdownRemark(
sort: { fields: [frontmatter___date], order: ASC }
limit: 1000
) {
edges {
node {
id
fields {
slug
}
frontmatter {
title
tags
}
}
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild(`Error occurred in GraphQL query`)
return
}
const posts = result.data.allMarkdownRemark.edges
// Create page for each post
posts.forEach((post, index) => {
const previousPostId = index === 0 ? null : posts[index - 1].node.id
const nextPostId = index === posts.length - 1 ? null : posts[index + 1].node.id
createPage({
path: post.node.fields.slug,
component: blogPostTemplate,
context: {
id: post.node.id,
previousPostId,
nextPostId,
},
})
})
// Create tag pages
const tagTemplate = path.resolve(`./src/templates/tag.js`)
const tags = new Set()
posts.forEach(post => {
if (post.node.frontmatter.tags) {
post.node.frontmatter.tags.forEach(tag => tags.add(tag))
}
})
tags.forEach(tag => {
createPage({
path: `/tags/${tag}/`,
component: tagTemplate,
context: {
tag,
},
})
})
}
```
### Advanced Optimization and Performance Settings
```jsx
// src/templates/blog-post.js
import React from "react"
import { graphql, Link } from "gatsby"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import SEO from "../components/seo"
import Bio from "../components/bio"
const BlogPostTemplate = ({ data, location }) => {
const post = data.markdownRemark
const siteTitle = data.site.siteMetadata?.title || `Title`
const { previous, next } = data
// Image optimization processing
const featuredImage = getImage(post.frontmatter.featuredImage)
return (
<Layout location={location} title={siteTitle}>
<SEO
title={post.frontmatter.title}
description={post.frontmatter.description || post.excerpt}
image={featuredImage}
/>
<article
className="blog-post"
itemScope
itemType="http://schema.org/Article"
>
<header>
<h1 itemProp="headline">{post.frontmatter.title}</h1>
<p>{post.frontmatter.date}</p>
{featuredImage && (
<GatsbyImage
image={featuredImage}
alt={post.frontmatter.title}
style={{ marginBottom: `2rem` }}
/>
)}
</header>
<section
dangerouslySetInnerHTML={{ __html: post.html }}
itemProp="articleBody"
/>
<hr />
<footer>
<Bio />
</footer>
</article>
<nav className="blog-post-nav">
<ul
style={{
display: `flex`,
flexWrap: `wrap`,
justifyContent: `space-between`,
listStyle: `none`,
padding: 0,
}}
>
<li>
{previous && (
<Link to={previous.fields.slug} rel="prev">
← {previous.frontmatter.title}
</Link>
)}
</li>
<li>
{next && (
<Link to={next.fields.slug} rel="next">
{next.frontmatter.title} →
</Link>
)}
</li>
</ul>
</nav>
</Layout>
)
}
export default BlogPostTemplate
export const query = graphql`
query BlogPostBySlug(
$id: String!
$previousPostId: String
$nextPostId: String
) {
site {
siteMetadata {
title
}
}
markdownRemark(id: { eq: $id }) {
id
excerpt(pruneLength: 160)
html
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
description
tags
featuredImage {
childImageSharp {
gatsbyImageData(
width: 1200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
previous: markdownRemark(id: { eq: $previousPostId }) {
fields {
slug
}
frontmatter {
title
}
}
next: markdownRemark(id: { eq: $nextPostId }) {
fields {
slug
}
frontmatter {
title
}
}
}
`
// Deferred Static Generation (DSG) configuration
export async function config() {
return ({ params }) => {
// Defer old articles with DSG
return {
defer: shouldDeferGeneration(params.slug),
}
}
}
function shouldDeferGeneration(slug) {
// Articles older than 1 year are deferred
const oneYearAgo = new Date()
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1)
// In actual implementation, check article date
return checkPostDate(slug) < oneYearAgo
}
```