# How Does Hot Reloading Work in Gatsby Development? Webpack HMR, Schema Reloading, and Failure Modes

> Understand Gatsby hot reloading including Webpack HMR, schema reloading, and failure modes to keep your development workflow smooth and efficient. Learn how it works and what causes issues.

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

---

**Gatsby hot reloading combines Webpack Hot Module Replacement for instant JavaScript updates, a schema hot reloader for GraphQL changes, and a Socket.io hard-refresh fallback, though it fails due to compilation errors, missing update files, or system watcher limits.**

When you run `gatsby develop`, the framework provides instant visual feedback through a sophisticated hot reloading system that updates React components, GraphQL schemas, and HTML templates without manual refreshes. This system relies on three integrated layers—Webpack HMR, schema inference watchers, and WebSocket fallbacks—to minimize development friction. Understanding how these mechanisms work in the Gatsby source code is essential for diagnosing cases where live updates stop functioning.

## The Architecture of Gatsby Hot Reloading

Gatsby's development server coordinates three distinct systems to provide seamless hot reloading.

### Webpack HMR for Client-Side Assets

In [`packages/gatsby/src/utils/start-server.ts`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/utils/start-server.ts), the development server initializes Webpack Hot Module Replacement (HMR) by mounting `webpackHotMiddleware` with specific configuration parameters:

```typescript
webpackHotMiddleware(compiler, { 
  path: '/__webpack_hmr', 
  heartbeat: 10_000 
})

```

This establishes the `/__webpack_hmr` endpoint where the browser polls for JavaScript and CSS updates every ten seconds. The `webpack-dev-middleware` serves compiled assets from memory rather than disk, ensuring rapid rebuilds when source files change.

When you edit a React component, Webpack recompiles the affected module and emits `*.hot-update.json` and `*.hot-update.js` chunks. The client-side HMR runtime downloads these files via the middleware endpoint and applies the changes using React Refresh, preserving component state without a full page reload.

### Schema Hot Reloader for GraphQL Updates

Data-driven changes trigger a separate path through [`packages/gatsby/src/bootstrap/schema-hot-reloader.ts`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/bootstrap/schema-hot-reloader.ts). The `bootstrapSchemaHotReloader()` function initializes the system by capturing a snapshot of the current GraphQL inference metadata and registering Redux event listeners.

This module listens for two specific events: `SET_SCHEMA` and `API_RUNNING_QUEUE_EMPTY`. When file changes occur outside the standard sourcing cycle—such as editing Markdown front-matter—the `API_RUNNING_QUEUE_EMPTY` event fires, triggering the debounced `maybeRebuildSchema` function configured with a **1000ms delay**.

The `maybeRebuildSchema` workflow performs three critical operations:

1. **Comparison**: Checks if `inferredTypesChanged` detects structural differences in the GraphQL schema.
2. **Rebuild**: Calls `rebuild()` to regenerate the schema if changes exist.
3. **Query Execution**: Invokes `updateStateAndRunQueries()` to re-run page queries against the new schema.

### Socket.io Hard-Refresh Fallback

Certain changes cannot apply through HMR. In [`start-server.ts`](https://github.com/gatsbyjs/gatsby/blob/main/start-server.ts), Gatsby uses **chokidar** to watch HTML template files and configuration changes:

```typescript
chokidar.watch(watchGlobs).on(`change`, async () => {
  await createIndexHtml(indexHTMLActivity)
  socket?.to(`clients`).emit(`reload`)
})

```

When these files change, the server emits a `"reload"` event via Socket.io (managed in [`packages/gatsby/src/utils/websocket-manager.ts`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/utils/websocket-manager.ts)), forcing connected browsers to perform a full page refresh to load the new HTML structure.

## Common Hot Reload Failures and Root Causes

Hot reloading in Gatsby can fail silently or explicitly due to several architectural constraints.

### Webpack Compilation Errors

Syntax errors, missing imports, or loader misconfigurations prevent Webpack from emitting hot-update files. When compilation fails, the HMR client cannot fetch updates, leaving the browser displaying the stale version or the error overlay. The system requires a successful compilation to generate the `*.hot-update.json` manifest.

### Missing Hot-Update Files

If the development server restarts while the browser awaits an update, the in-memory hot-update chunks disappear. The fallback route in [`start-server.ts`](https://github.com/gatsbyjs/gatsby/blob/main/start-server.ts) handles this by returning 404 for any `*.hot-update.json` request:

```typescript
app.use(/.*\.hot-update\.json$/i, (_, res) => {
  res.status(404).end()
})

```

This forces a hard refresh, but if the socket connection is also disrupted, the browser may hang until manually reloaded.

### Socket.io Disconnection

Changes to HTML templates or [`gatsby-ssr.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-ssr.js) rely on the Socket.io channel to trigger full page reloads. Network interruptions, proxy misconfigurations, or custom `onCreateDevServer` implementations that replace the websocket manager can sever this connection, leaving the browser out of sync with the server state.

### Debounce Suppression and Unchanged Metadata

The `maybeRebuildSchema` function uses a **1-second debounce** to batch rapid file changes. If you edit data files multiple times per second, the system coalesces these events. Additionally, if `inferredTypesChanged` returns false—indicating the schema structure remains identical despite content changes—Gatsby skips the rebuild entirely, which can mask updates where only field values change without type structural changes.

### File System Watcher Limits

Large projects may exceed operating system limits for file watchers (e.g., `max_user_watches` on Linux). When chokidar cannot watch specific source directories, changes to those files go undetected, breaking the hot reload trigger entirely.

### Cache Corruption

Stale data in the `.cache` or `public` directories can cause the development server to serve outdated assets despite source changes. This manifests as apparent hot reload failures where the browser refreshes but displays old content.

## Code Examples: Hot Reloading in Practice

### Hot Reloading React Components

Editing a page component triggers the Webpack HMR pipeline:

```javascript
// src/pages/index.js
import * as React from "react"

export default function Home() {
  return <h1>Updated Headline</h1>
}

```

After saving, Webpack compiles the delta, serves it via the HMR endpoint, and React Refresh swaps the component while preserving state.

### Schema Changes via Markdown

Adding a new field to Markdown front-matter activates the schema hot reloader:

```markdown
---
title: "Post"
category: "Tutorial"  # New field

---

Content here.

```

The file watcher detects the change, `maybeRebuildSchema` validates that the `category` field alters the inferred GraphQL type, and Gatsby rebuilds the schema and re-runs queries automatically.

### HTML Template Modifications

Changes to [`src/html.js`](https://github.com/gatsbyjs/gatsby/blob/main/src/html.js) require a full page reload:

```javascript
// src/html.js
export default function Html({ body }) {
  return (
    <html>
      <head>
        <meta charSet="utf-8" />
        <title>Updated Site Title</title>
      </head>
      <body dangerouslySetInnerHTML={{ __html: body }} />
    </html>
  )
}

```

The chokidar watcher notices the file change, regenerates the HTML shell via `createIndexHtml`, and emits the Socket.io reload event to refresh all connected clients.

## Summary

- **Webpack HMR** handles JavaScript and CSS updates via the `/__webpack_hmr` endpoint with a 10-second heartbeat, serving compiled assets from memory.
- **Schema Hot Reloader** monitors data changes through `bootstrapSchemaHotReloader()` and debounced rebuilds (1s delay) via `maybeRebuildSchema`, checking `inferredTypesChanged` to avoid unnecessary work.
- **Socket.io Fallback** provides hard refreshes for HTML template changes when HMR cannot apply updates, implemented in [`packages/gatsby/src/utils/websocket-manager.ts`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/utils/websocket-manager.ts).
- **Common failures** include Webpack compilation errors preventing update generation, missing hot-update files after server restarts, socket disconnections breaking the reload channel, debounce logic suppressing rapid schema changes, inference metadata checks skipping validation, and OS file watcher limits preventing change detection.

## Frequently Asked Questions

### Why does Gatsby hot reloading trigger a full page refresh instead of updating modules?

A full refresh occurs when Webpack encounters a hard dependency boundary it cannot hot-swap, such as changes to the HTML template in [`src/html.js`](https://github.com/gatsbyjs/gatsby/blob/main/src/html.js) or [`gatsby-ssr.js`](https://github.com/gatsbyjs/gatsby/blob/main/gatsby-ssr.js), or when the HMR client cannot locate the `*.hot-update.json` manifest file. The Socket.io channel explicitly emits a `"reload"` event for these cases to ensure the browser loads the latest server-rendered markup.

### Why aren't my GraphQL schema changes hot reloading when I edit Markdown files?

If `maybeRebuildSchema` determines that `inferredTypesChanged` returns false—meaning the structural shape of the schema remains identical despite content updates—it skips the rebuild to optimize performance. Additionally, if your edits occur within the 1000ms debounce window of a previous change, the system batches them and may appear to ignore rapid successive modifications.

### What causes the "Cannot find update" error in Gatsby development mode?

This error typically appears when the development server restarts or crashes after Webpack compiles a change but before the browser downloads the hot-update chunks. The fallback route in [`start-server.ts`](https://github.com/gatsbyjs/gatsby/blob/main/start-server.ts) returns a 404 for missing [`hot-update.json`](https://github.com/gatsbyjs/gatsby/blob/main/hot-update.json) files, which forces a hard refresh, but network latency or socket disconnection can delay this recovery.

### How do I fix hot reloading when it stops working entirely?

First, verify that your terminal shows no Webpack compilation errors blocking the build. Next, clear potential cache corruption by deleting the `.cache` and `public` directories, then restart the development server. If using a custom proxy or Docker container, ensure the WebSocket connection for Socket.io remains unblocked, and check that your operating system's file watcher limits (`fs.inotify.max_user_watches` on Linux) are sufficient for your project size.