# How Gatsby's GraphQL Data Layer Works: Static Queries vs Page Queries Explained

> Understand Gatsby's GraphQL data layer. Learn the difference between static queries and page queries and how they fetch data at build time for your site.

- Repository: [Gatsby/gatsby](https://github.com/gatsbyjs/gatsby)
- Tags: deep-dive
- Published: 2026-03-06

---

**Gatsby's GraphQL data layer extracts, compiles, and executes GraphQL queries at build time, with page queries running once per page and injecting data via `props.data`, while static queries run once globally and share results through React context.**

Gatsby's GraphQL data layer serves as the central nervous system of the `gatsbyjs/gatsby` repository, transforming disparate data sources into a unified schema that powers both static sites and server-side rendering. During the build process, Gatsby scans your source code to extract GraphQL documents, validates them against the central schema, and executes them through a sophisticated pipeline involving the `query-compiler` and `file-parser` modules. Understanding how **page queries** and **static queries** differ in scope, execution, and runtime consumption is essential for optimizing site architecture.

## The Query Extraction and Compilation Pipeline

Gatsby's data layer begins with **file scanning** and **AST parsing**. The `parseQueries` function in [`packages/gatsby/src/query/query-compiler.js`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/query/query-compiler.js) glob-matches all `.js`, `.tsx`, and `.mdx` files that import `graphql`, then `parseToAst` in [`file-parser.js`](https://github.com/gatsbyjs/gatsby/blob/main/file-parser.js) uses Babel to parse each file into an AST. The `findGraphQLTags` walker locates tagged template literals (`` graphql`...` ``) and extracts three document types: **page queries**, **static queries**, and **config queries**.

Anonymous queries receive deterministic names via `generateQueryName` using the pattern `<type>-<slug>-<hash>`. The [`query-compiler.js`](https://github.com/gatsbyjs/gatsby/blob/main/query-compiler.js) module then validates these queries against the compiled schema, resolves fragments, and deduplicates requests. Finally, the `GraphQLRunner` executes validated queries and caches results using SHA-1 hashes stored in LMDB to skip redundant work on subsequent builds.

## How Page Queries Work

Page queries are scoped to individual page components and support dynamic variables derived from URL context.

- **Scope**: Exported as `export const query = graphql\`...\`` from files in `src/pages` or templates created via `createPage`.
- **Execution**: Run **once per page instance** by [`query-runner.ts`](https://github.com/gatsbyjs/gatsby/blob/main/query-runner.ts), with results written to `public/page-data/<path>/page-data.json`.
- **Access**: Injected directly into the page component as `props.data`.

The [`query-runner.ts`](https://github.com/gatsbyjs/gatsby/blob/main/query-runner.ts) module handles the execution lifecycle, distinguishing page queries via `queryJob.queryType === 'page'`. These queries can reference `$variables` passed through the `pageContext` property in [`gatsby-node.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-node.js), enabling dynamic filtering and pagination.

```javascript
// src/pages/blog.js
import React from "react"
import { graphql } from "gatsby"

export const query = graphql`
  query BlogPage($skip: Int!, $limit: Int!) {
    allMarkdownRemark(skip: $skip, limit: $limit) {
      edges {
        node {
          frontmatter { title }
          excerpt
        }
      }
    }
  }
`

export default function BlogPage({ data, pageContext }) {
  const posts = data.allMarkdownRemark.edges
  return (
    <ul>
      {posts.map(({ node }) => (
        <li key={node.frontmatter.title}>{node.frontmatter.title}</li>
      ))}
    </ul>
  )
}

```

## How Static Queries Work

Static queries provide global data access for components that are not page components.

- **Scope**: Defined inside any React component using the `useStaticQuery` hook or the legacy `<StaticQuery>` component.
- **Execution**: Run **once per build** and cached globally in `StaticQueryContext`.
- **Access**: Retrieved via `useStaticQuery` or `<StaticQuery>`, which read from the React context populated by [`query-result-store.js`](https://github.com/gatsbyjs/gatsby/blob/main/query-result-store.js).
- **Limitations**: Cannot reference `$variables` dependent on page URLs.

The runtime implementation lives in [`packages/gatsby/cache-dir/static-query.js`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/cache-dir/static-query.js). The `useStaticQuery` hook reads from `StaticQueryContext`, throwing an error if the query result is missing from the global cache.

```javascript
// packages/gatsby/cache-dir/static-query.js
export const useStaticQuery = query => {
  const context = React.useContext(StaticQueryContext)
  if (context[query]?.data) return context[query].data
  throw new Error(`The result of this StaticQuery could not be fetched…`)
}

```

### Static Query Example with Hooks

```javascript
// src/components/site-metadata.js
import React from "react"
import { graphql, useStaticQuery } from "gatsby"

export default function SiteMetadata() {
  const data = useStaticQuery(graphql`
    query SiteInfo {
      site {
        siteMetadata {
          title
          description
        }
      }
    }
  `)

  return (
    <header>
      <h1>{data.site.siteMetadata.title}</h1>
      <p>{data.site.siteMetadata.description}</p>
    </header>
  )
}

```

### Legacy StaticQuery Component

```javascript
import React from "react"
import { StaticQuery, graphql } from "gatsby"

export default function Footer() {
  return (
    <StaticQuery
      query={graphql`
        query FooterInfo {
          site {
            siteMetadata { author }
          }
        }
      `}
      render={data => <footer>© {data.site.siteMetadata.author}</footer>}
    />
  )
}

```

## Key Implementation Files

The following source files constitute the core of Gatsby's GraphQL data layer:

| File | Role |
|------|------|
| [`packages/gatsby/src/query/file-parser.js`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/query/file-parser.js) | Parses source files, extracts static, page, and config queries, auto-generates names via `findGraphQLTags` and `generateQueryName`. |
| [`packages/gatsby/src/query/query-compiler.js`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/query/query-compiler.js) | Validates extracted queries, resolves fragments, deduplicates, and returns processed query maps. |
| [`packages/gatsby/src/query/query-runner.ts`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/query/query-runner.ts) | Executes compiled queries, hashes results with SHA-1, writes JSON to `public/page-data`, and updates the Redux store. |
| [`packages/gatsby/cache-dir/static-query.js`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/cache-dir/static-query.js) | Runtime implementation of `useStaticQuery` and `<StaticQuery>`; supplies data via `StaticQueryContext`. |
| [`packages/gatsby/cache-dir/query-result-store.js`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/cache-dir/query-result-store.js) | Holds the in-memory map of static-query results for browser and SSR bundles. |
| [`packages/gatsby/src/query/query-watcher.ts`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/query/query-watcher.ts) | Watches for file changes during development and triggers re-compilation of queries. |

## Summary

- **Page queries** execute per-page, support URL-based variables via `pageContext`, and inject data through `props.data`.
- **Static queries** execute once globally, cache results in `StaticQueryContext`, and provide site-wide data via `useStaticQuery` or `<StaticQuery>`.
- The [`file-parser.js`](https://github.com/gatsbyjs/gatsby/blob/main/file-parser.js) module extracts all GraphQL documents using Babel AST parsing.
- The [`query-compiler.js`](https://github.com/gatsbyjs/gatsby/blob/main/query-compiler.js) validates and optimizes queries before execution.
- The [`query-runner.ts`](https://github.com/gatsbyjs/gatsby/blob/main/query-runner.ts) handles execution, SHA-1 hashing, and LMDB caching to avoid redundant processing.
- All query results are written to `public/page-data/` for runtime consumption.

## Frequently Asked Questions

### Can static queries accept variables from page context?

No, static queries cannot reference `$variables` that depend on a page's URL or `pageContext` because they are executed once per build and shared globally across all components. Page queries, by contrast, accept variables passed through `createPage`'s context property and execute individually for each page instance. This architectural distinction makes static queries ideal for global site metadata and page queries essential for dynamic content listings.

### Where does Gatsby store query results during the build process?

The [`query-runner.ts`](https://github.com/gatsbyjs/gatsby/blob/main/query-runner.ts) module writes all query results to JSON files under `public/page-data/` and maintains an in-memory cache using LMDB with SHA-1 hashed results. Static query results populate the `StaticQueryContext` React context available via `useStaticQuery`, while page query results are injected directly into page components as `props.data` at runtime.

### How does Gatsby handle anonymous GraphQL queries?

If a query lacks an explicit name, the `generateQueryName` function in [`file-parser.js`](https://github.com/gatsbyjs/gatsby/blob/main/file-parser.js) automatically creates a deterministic identifier using the pattern `<type>-<slug>-<hash>`. This ensures every extracted document has a unique reference for caching and execution purposes while maintaining consistent builds across environments.

### How does the development server watch for query changes?

The [`query-watcher.ts`](https://github.com/gatsbyjs/gatsby/blob/main/query-watcher.ts) file monitors source files for modifications during development, triggering re-extraction via [`file-parser.js`](https://github.com/gatsbyjs/gatsby/blob/main/file-parser.js) and re-compilation through [`query-compiler.js`](https://github.com/gatsbyjs/gatsby/blob/main/query-compiler.js) whenever GraphQL documents change. This hot-reloading capability ensures the GraphQL data layer reflects code changes without requiring a full rebuild.