# How to Configure HMR in Cordis With Debounce, Root, and Ignore Options

> Learn to configure HMR in Cordis using root, debounce, and ignore options. Optimize your development workflow with efficient file watching. Master Cordis HMR settings now.

- Repository: [Cordiverse/cordis](https://github.com/cordiverse/cordis)
- Tags: how-to-guide
- Published: 2026-08-23

---

**Configure Cordis HMR by setting `root` directories to watch, `debounce` milliseconds to batch rapid changes, and `ignored` glob patterns to exclude files—options passed directly to the underlying `chokidar` watcher.**

The `@cordisjs/plugin-hmr` package provides hot module replacement for Cordis applications, allowing plugins to reload automatically when source files change. These three configuration options control precisely which files trigger reloads and how quickly those reloads occur.

## Understanding the Core HMR Configuration Options

The HMR plugin accepts a configuration object that extends standard **chokidar** watcher options. According to the source code in [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts), three fields directly impact developer experience:

| Option | Type | Default | Purpose |
|--------|------|---------|---------|
| `root` | `string[]` | `['.']` | Directories to monitor, relative to the base path |
| `debounce` | `number` | `100` | Milliseconds to wait before triggering reload after last change |
| `ignored` | `string[]` | `['**/node_modules', '**/.*', 'cache', 'data']` | Glob patterns excluded from watching |

These defaults are defined in the **Hmr.Config** interface at lines 8-13 of [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts).

### How `root` Controls the Watch Scope

The `root` option determines where `chokidar.watch()` begins scanning. In [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts) lines 8-13, the watcher initializes with:

```typescript
const watcher = chokidar.watch(this.config.root, {
  ignored: this.config.ignored,
  // ...
})

```

Multiple directories can be specified as an array. Paths are resolved relative to the Cordis base directory.

### How `debounce` Batches Rapid File Changes

The `debounce` value wraps the `partialReload()` method to prevent excessive reloads during rapid edits. At lines 25-26 of [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts):

```typescript
this.debouncedReload = this.ctx.debounce(this.partialReload.bind(this), this.config.debounce)

```

The `ctx.debounce` utility itself is provided by `@cordisjs/plugin-timer`, implemented at lines 134-135 of [`packages/timer/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/timer/src/index.ts). The test suite in [`packages/hmr/tests/index.spec.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/tests/index.spec.ts) (lines 66-82) verifies that multiple changes within the debounce window trigger only a single reload.

### How `ignored` Filters Unwanted Files

Files matching any pattern in `ignored` are excluded before the watcher emits events. The plugin uses `picomatch` for glob filtering as part of the chokidar initialization. Default patterns exclude `node_modules`, hidden files, and common data directories.

## Configuring HMR in a Cordis YAML File

The most common approach uses a **Cordis YAML** configuration file. The plugin configuration is read during context initialization through `ctx.loader.entries()`.

```yaml

# cordis.yml

- id: timer
  name: '@cordisjs/plugin-timer'  # Required: provides ctx.debounce()

- id: hmr
  name: '@cordisjs/plugin-hmr'
  config:
    # Watch only these directories

    root:
      - src
    
    # Exclude from watching

    ignored:
      - '**/node_modules'
      - '**/.git'
      - '**/*.js'        # Skip compiled output if using TypeScript

      - 'dist/**'
    
    # Batch changes within 50ms

    debounce: 50

```

## Configuring HMR Programmatically

For dynamic or environment-specific setups, pass the configuration object directly to `ctx.plugin()`:

```typescript
import { Context } from 'cordis';
import Loader from '@cordisjs/plugin-loader';
import Hmr from '@cordisjs/plugin-hmr';
import Timer from '@cordisjs/plugin-timer';

const ctx = new Context();

async function main() {
  // Required dependency: provides debounce utility
  await ctx.plugin(Timer);
  
  // Provides plugin loading infrastructure
  await ctx.plugin(Loader);
  
  // Configure HMR with custom options
  await ctx.plugin(Hmr, {
    root: ['src', 'lib'],                    // Monitor multiple directories
    ignored: [
      '**/node_modules',
      '**/.git',
      'dist/**',
      '**/*.test.ts'                         // Ignore test files
    ],
    debounce: 150                            // 150ms batching window
  });
  
  await ctx.start();
}

main();

```

## Key Source Files and Implementation Details

| File | Lines | Purpose |
|------|-------|---------|
| [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts) | 8-13, 25-26 | Config interface, watcher creation, debounce wrapper |
| [`packages/hmr/tests/index.spec.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/tests/index.spec.ts) | 66-82 | Debounce behavior verification |
| [`packages/timer/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/timer/src/index.ts) | 134-135 | `ctx.debounce()` implementation |
| [`packages/hmr/tests/cordis.yml`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/tests/cordis.yml) | — | Example YAML configuration |

## Performance and Behavior Considerations

- **Lower `debounce` values** (50ms or less) feel more responsive but may trigger multiple reloads during rapid saves
- **Higher `debounce` values** (200ms+) reduce reload frequency but introduce noticeable delay
- **Restrictive `root` paths** improve startup performance by reducing initial scan scope
- **Comprehensive `ignored` patterns** prevent unnecessary reloads from build artifacts, lockfiles, and editor temporary files

The test suite at [`packages/hmr/tests/index.spec.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/tests/index.spec.ts) demonstrates that proper configuration of these three options is essential for stable development workflows with large codebases.

## Summary

- **`root`** specifies which directories to watch; defaults to current working directory
- **`debounce`** controls reload batching in milliseconds; defaults to 100ms
- **`ignored`** filters files using glob patterns; excludes `node_modules` and hidden files by default
- All options are passed directly to `chokidar.watch()` in [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts)
- The `@cordisjs/plugin-timer` dependency provides the underlying `ctx.debounce()` utility

## Frequently Asked Questions

### What happens if I don't install `@cordisjs/plugin-timer`?

The HMR plugin will fail to initialize because `ctx.debounce()` is undefined. The timer plugin is a **required peer dependency** that provides the debounce utility used at line 25 of [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts).

### Can I use absolute paths in the `root` option?

Yes. The `root` array accepts any valid path that `chokidar.watch()` can resolve. Relative paths are resolved against the Cordis base directory; absolute paths are used as-is.

### Why are my changes not triggering reloads despite correct `root` settings?

Check your `ignored` patterns. The default configuration excludes all dotfiles (`**/.*`). If your source files are in a hidden directory or match an ignored glob, they will not emit change events. Verify against the `picomatch` filtering in the watcher initialization.

### Does `debounce` affect the initial load or only subsequent reloads?

The `debounce` option only affects **hot reloads** of already-loaded plugins. Initial plugin loading during context startup is not debounced. The debounced `partialReload()` wrapper created at lines 25-26 of [`packages/hmr/src/index.ts`](https://github.com/cordiverse/cordis/blob/main/packages/hmr/src/index.ts) is invoked exclusively on file change events.