# How the CRA Development Server Handles Browser Refresh on Errors

> Discover how the CRA development server manages browser refreshes on errors. Learn about Webpack dev-server client, WebSockets, HMR, and full reloads for a smoother development experience.

- Repository: [Meta/create-react-app](https://github.com/facebook/create-react-app)
- Tags: internals
- Published: 2026-02-26

---

**Create React App uses a custom Webpack dev-server client that opens a WebSocket connection to receive compilation status messages, preferring hot module replacement (HMR) to preserve state but forcing a full `window.location.reload()` whenever HMR fails, runtime errors cannot be recovered by Fast Refresh, or static assets change.**

During development, Create React App (CRA) runs a custom client-side handler located in [`packages/react-dev-utils/webpackHotDevClient.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-dev-utils/webpackHotDevClient.js) that manages the browser's response to compilation events. Instead of blindly reloading on every file change, the development server communicates via WebSocket to intelligently decide between applying hot updates or performing a full browser refresh when errors occur. This mechanism balances development speed with UI consistency by attempting to preserve component state while ensuring error-free renders.

## WebSocket Communication and Compilation Messages

The development server establishes a persistent WebSocket connection to the browser client. When Webpack finishes compilation, the server emits JSON messages describing the build state, which the client processes in `connection.onmessage` within [`webpackHotDevClient.js`](https://github.com/facebook/create-react-app/blob/main/webpackHotDevClient.js).

The client recognizes specific message types that determine whether to trigger HMR or a full reload:

- **`hash`** – Signals a new compilation hash, stored for update availability checks
- **`ok` / `still-ok`** – Indicates successful compilation with no errors
- **`warnings`** – Successful compilation containing lint or build warnings
- **`errors`** – Compilation failed with syntax or build errors
- **`content-changed`** – Static assets or files outside the `src` directory changed

## Hot Module Replacement vs. Full Page Reload

The decision between hot patching and hard reloading lives primarily in the `tryApplyUpdates` function (lines 45–78 of [`webpackHotDevClient.js`](https://github.com/facebook/create-react-app/blob/main/webpackHotDevClient.js)). This function evaluates multiple conditions before falling back to `window.location.reload()`.

### Successful Compilation Flow

When the client receives `ok` or `still-ok` messages, it invokes `handleSuccess()`. If this is not the initial compilation (`!isFirstCompilation`), the client attempts HMR through `tryApplyUpdates()`:

1. Checks if `module.hot` exists and status is `idle`
2. Verifies `isUpdateAvailable()` using the compilation hash
3. Calls `module.hot.check(true, callback)` to fetch updated modules

If `module.hot.check` succeeds and Fast Refresh can accept the changes, the update applies without reloading. The error overlay dismisses via `ErrorOverlay.dismissBuildError()`.

### Compile-Time Error Handling

When the client receives an `errors` message, it invokes `handleErrors()` which:

- Clears the console
- Formats messages using [`formatWebpackMessages.js`](https://github.com/facebook/create-react-app/blob/main/formatWebpackMessages.js)
- Displays the first error in the **react-error-overlay**
- **Does not reload the browser**

The client waits for the next successful compilation before attempting recovery. This preserves the error context for debugging while allowing the developer to fix issues without losing the current application state in the console.

### Runtime Error Recovery

Runtime errors trigger `hadRuntimeError = true` via the error overlay's `startReportingRuntimeErrors`. On the **next successful compile**, `tryApplyUpdates` detects this flag and checks `canAcceptErrors()` to determine if Fast Refresh can safely recover the component tree.

If `hadRuntimeError` is true but Fast Refresh cannot accept the error state, the client forces `window.location.reload()` to ensure a clean execution environment.

### Static Asset Changes

When the WebSocket receives `content-changed`, the client bypasses HMR entirely and calls `window.location.reload()` immediately. This handles changes to `public/` assets, [`index.html`](https://github.com/facebook/create-react-app/blob/main/index.html), or other files outside the hot-reloadable module graph.

## The Decision Logic in tryApplyUpdates

The core reload logic resides in `tryApplyUpdates`, which evaluates safety conditions before applying updates:

```javascript
// packages/react-dev-utils/webpackHotDevClient.js
function tryApplyUpdates(onHotUpdateSuccess) {
  if (!module.hot) {
    // No HMR support → hard reload
    window.location.reload();
    return;
  }

  if (!isUpdateAvailable() || !canApplyUpdates()) return;

  module.hot.check(true, (err, updatedModules) => {
    const haveErrors = err || hadRuntimeError;
    const needsForcedReload = !err && !updatedModules;

    // If we cannot apply the update safely, fall back to reload
    if ((haveErrors && !canAcceptErrors()) || needsForcedReload) {
      window.location.reload();
      return;
    }

    // Successful hot update
    if (typeof onHotUpdateSuccess === 'function') onHotUpdateSuccess();

    // Handle queued updates
    if (isUpdateAvailable()) tryApplyUpdates();
  });
}

```

The function forces a reload when:
- Webpack encounters an error during the check (`err` exists)
- A runtime error occurred and Fast Refresh cannot accept it (`hadRuntimeError && !canAcceptErrors()`)
- No modules were updated (`!updatedModules`), indicating a potential inconsistency

## Error Overlay Integration

The **react-error-overlay** package (located in [`packages/react-error-overlay/src/index.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-error-overlay/src/index.js)) provides the visual error interface. It records runtime errors through `ErrorOverlay.startReportingRuntimeErrors()` and displays compile-time errors via `ErrorOverlay.reportBuildError()`.

When `handleErrors()` processes compilation failures, it formats messages using [`formatWebpackMessages.js`](https://github.com/facebook/create-react-app/blob/main/formatWebpackMessages.js) before passing them to the overlay. After a successful hot update, `handleSuccess()` calls `ErrorOverlay.dismissBuildError()` to clear the overlay without reloading the page.

## Summary

- CRA's [`webpackHotDevClient.js`](https://github.com/facebook/create-react-app/blob/main/webpackHotDevClient.js) opens a WebSocket to receive real-time compilation status from the development server
- The client prefers **hot module replacement** through `tryApplyUpdates()` to preserve application state
- **Compile errors** display in the error overlay without triggering an immediate browser refresh
- **Runtime errors** set `hadRuntimeError = true`, forcing a reload on the next successful compile if Fast Refresh cannot recover
- **Static asset changes** trigger immediate `window.location.reload()` via the `content-changed` message handler
- The reload decision depends on `module.hot.check()` results, error presence, and Fast Refresh capability checks in `canAcceptErrors()`

## Frequently Asked Questions

### Does CRA reload the browser on every code change?

No. Create React App attempts hot module replacement first. The browser only reloads if HMR is unavailable, the update cannot be applied safely, or you modify static assets outside the Webpack module graph. Successful JavaScript edits typically update components without losing state.

### What happens if a runtime error occurs during development?

The react-error-overlay captures the error and sets `hadRuntimeError = true`. The browser does not reload immediately. On the next successful compilation, `tryApplyUpdates` checks `canAcceptErrors()` to determine if Fast Refresh can handle the recovery. If not, it forces `window.location.reload()` to ensure a clean state.

### Why does the page reload when I edit certain files but not others?

Changes to JavaScript/TypeScript files in the `src` directory attempt HMR first. However, if you modify files in the `public` folder, [`index.html`](https://github.com/facebook/create-react-app/blob/main/index.html), or other static assets, the server sends a `content-changed` message. The client handles this by calling `window.location.reload()` immediately, as these changes cannot be hot-patched into the running application.

### Can I disable the automatic browser refresh behavior in CRA?

The automatic refresh is integral to the [`webpackHotDevClient.js`](https://github.com/facebook/create-react-app/blob/main/webpackHotDevClient.js) implementation and cannot be disabled through standard configuration options. The client automatically decides between HMR and full reload based on compilation health. To prevent reloads, you would need to eject and modify the `tryApplyUpdates` logic or handle WebSocket messages manually, though this is not recommended for standard development workflows.