# How to Configure Hot Module Replacement (HMR) in Storybook: Complete Vite Builder Guide

> Learn to configure Hot Module Replacement HMR in Storybook with the Vite builder. Customize port, protocol, or disable HMR via viteFinal in your main.ts file for faster development.

- Repository: [Storybook/storybook](https://github.com/storybookjs/storybook)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Storybook enables Hot Module Replacement (HMR) automatically when using the Vite builder, allowing you to configure the HMR port, protocol, or disable it entirely through the `viteFinal` function in your [`.storybook/main.ts`](https://github.com/storybookjs/storybook/blob/main/.storybook/main.ts) configuration file.**

Storybook's development server provides first-class support for Hot Module Replacement (HMR) to instantly reflect changes to components, stories, and addons without full page reloads. When you configure Hot Module Replacement (HMR) in Storybook using the default Vite builder—as implemented in the `storybookjs/storybook` repository—the system wires the HMR socket directly into the existing dev server. This guide explains how to customize HMR behavior, from basic port configuration to complete disablement for CI environments.

## Understanding HMR in Storybook's Vite Builder

The Vite builder creates a dev server using `createViteServer` in [`code/builders/builder-vite/src/vite-server.ts`](https://github.com/storybookjs/storybook/blob/main/code/builders/builder-vite/src/vite-server.ts). This file injects the HMR configuration directly into Vite's server options, binding the HMR socket to the same HTTP server that serves the Storybook UI.

### Core HMR Wiring

In [`code/builders/builder-vite/src/vite-server.ts`](https://github.com/storybookjs/storybook/blob/main/code/builders/builder-vite/src/vite-server.ts), the `hmr` object is configured to use the existing dev server instance and a specific port. This ensures that HMR updates flow through the same connection as your Storybook preview, avoiding cross-origin issues and port conflicts.

```typescript
// code/builders/builder-vite/src/vite-server.ts
server: {
  middlewareMode: true,
  hmr: {
    port,
    server: devServer,
  },
  fs: {
    strict: true,
  },
},

```

## How to Configure HMR in Your Storybook Project

You can customize HMR behavior through the `viteFinal` configuration hook in [`.storybook/main.ts`](https://github.com/storybookjs/storybook/blob/main/.storybook/main.ts). This function receives the final Vite configuration object and allows you to modify the `server.hmr` options before the dev server starts.

### Basic Configuration (Default)

By default, Storybook automatically enables HMR when using the Vite builder. No additional configuration is required in your [`main.ts`](https://github.com/storybookjs/storybook/blob/main/main.ts) file. The builder automatically assigns an available port and wires it to the dev server according to the documentation in [`docs/_snippets/storybook-builder-api-configuration-options.md`](https://github.com/storybookjs/storybook/blob/main/docs/_snippets/storybook-builder-api-configuration-options.md).

```typescript
// .storybook/main.ts – minimal config, HMR works automatically
import type { StorybookConfig } from '@storybook/react-vite';

export default {
  framework: '@storybook/react-vite',
  core: { builder: '@storybook/builder-vite' },
} satisfies StorybookConfig;

```

### Custom HMR Port and Protocol

To configure a specific HMR port or change the WebSocket protocol, modify the `server.hmr` object in `viteFinal`. This is useful when running Storybook behind a proxy or when the default port is already occupied.

```typescript
// .storybook/main.ts – custom HMR options
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  framework: '@storybook/react-vite',
  core: { builder: '@storybook/builder-vite' },
  viteFinal: async (config) => {
    config.server ??= {};
    config.server.hmr = {
      port: 3001,          // custom port
      protocol: 'ws',      // force WebSocket protocol
    };
    return config;
  },
};

export default config;

```

### Disabling HMR for CI or Testing

For continuous integration environments or performance testing where instant updates are unnecessary, you can completely disable HMR by setting `server.hmr` to `false`. This prevents the WebSocket connection from being established and reduces resource consumption.

```typescript
// .storybook/main.ts – turn off HMR
import type { StorybookConfig } from '@storybook/react-vite';

export default {
  framework: '@storybook/react-vite',
  core: { builder: '@storybook/builder-vite' },
  viteFinal: async (config) => {
    config.server ??= {};
    config.server.hmr = false; // disables HMR
    return config;
  },
} satisfies StorybookConfig;

```

## HMR for Storybook Addons

Addons running inside Storybook inherit the same HMR capabilities as your stories. The behavior differs slightly depending on whether the addon is developed locally alongside your project or distributed as a standalone package, as documented in `docs/addons/addon-knowledge-base.mdx`.

### Local Addons

Local addons—those installed via file references or workspaces—automatically receive HMR updates without additional configuration. According to the addon knowledge base, changes to local addon code trigger instant updates in the Storybook UI because they share the same Vite dev server instance.

### Standalone Addons

For standalone addons developed in separate repositories, enable HMR by running a watch script that rebuilds the addon while Storybook stays alive. While the addon rebuilds, Storybook picks up the new code through the file system. Configure this in your addon's [`package.json`](https://github.com/storybookjs/storybook/blob/main/package.json):

```json
// addon/package.json
{
  "name": "my-addon",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "start": "npm run build -- --watch"
  },
  "peerDependencies": {
    "@storybook/addons": "^7.0.0"
  }
}

```

Run `npm start` in the addon's folder, then launch Storybook in the parent repository. The addon will hot-reload as you edit its source code.

## How Storybook Handles HMR in Story Files

Story files themselves should not be treated as HMR modules because Storybook already handles reloading stories at a higher level. To prevent duplicate reloads or conflicting HMR boundaries, the Vite builder includes a plugin that removes `import.meta.hot.accept` calls that may be emitted by bundlers.

In [`code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts`](https://github.com/storybookjs/storybook/blob/main/code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts), the plugin uses a regular expression to replace HMR acceptance calls with no-op functions:

```typescript
// code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts
s.replace(/import\.meta\.hot\.accept\w*/, '(function hmrBoundaryNoop(){})');

```

This ensures that changes to `.stories.js|ts|jsx|tsx` files trigger a story reload through Storybook's internal mechanism rather than Vite's module replacement, maintaining consistent state and avoiding hydration mismatches.

## Summary

- **Automatic HMR**: Storybook enables Hot Module Replacement automatically when using the Vite builder via `createViteServer` in [`code/builders/builder-vite/src/vite-server.ts`](https://github.com/storybookjs/storybook/blob/main/code/builders/builder-vite/src/vite-server.ts).
- **Configuration**: Customize HMR port, protocol, or disable it entirely through the `viteFinal` function in [`.storybook/main.ts`](https://github.com/storybookjs/storybook/blob/main/.storybook/main.ts).
- **Addon Support**: Local addons inherit HMR automatically; standalone addons require a watch script for development.
- **Story Handling**: The Vite builder strips HMR boundaries from story files via [`strip-story-hmr-boundaries.ts`](https://github.com/storybookjs/storybook/blob/main/strip-story-hmr-boundaries.ts) to prevent duplicate reloads.

## Frequently Asked Questions

### Is HMR enabled by default in Storybook?

Yes, when using the Vite builder (the default in recent Storybook versions), HMR is enabled automatically. The builder configures the HMR socket to share the same port as the Storybook dev server in [`code/builders/builder-vite/src/vite-server.ts`](https://github.com/storybookjs/storybook/blob/main/code/builders/builder-vite/src/vite-server.ts), requiring no manual setup for standard development workflows.

### How do I change the HMR port in Storybook?

Modify the `server.hmr` option in the `viteFinal` function within your [`.storybook/main.ts`](https://github.com/storybookjs/storybook/blob/main/.storybook/main.ts) file. Set the `port` property to your desired port number to avoid conflicts with other services or to comply with corporate firewall rules.

### Can I disable HMR in Storybook?

Yes, set `server.hmr` to `false` in your `viteFinal` configuration. This prevents the WebSocket connection from being established and is particularly useful for CI environments, static builds, or when debugging issues that might be caused by module replacement side effects.

### Do Storybook addons support HMR?

Yes, local addons that are part of your workspace receive HMR automatically through the same Vite dev server instance. Standalone addons developed in separate repositories can support HMR by running a watch script (such as `npm run build -- --watch`) that rebuilds the addon while Storybook remains running, allowing the dev server to detect and serve the updated files.