Gatsby

Mature React + GraphQL based SSG. Rich plugin ecosystem with over 55k GitHub stars.

言語:JavaScript/TypeScript
フレームワーク:React
ビルド速度:Medium
GitHub Stars:55k
初回リリース:2015
人気ランキング:第3位

トレンド・動向

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 } ```