electron-reloader: restarting the main process and reloading windows on file changes
Simple auto-reloading for Electron apps during development
At a glance
- What is it?
- electron-reloader watches your Electron app's directory and restarts the main process when a main process file changes while reloading the windows when anything else changes. It suits a developer who wants a two-line setup instead of a custom file watcher, and it requires an ESM main process and Electron 44 or later.
- Who is it for?
- electron-reloader fits a developer on Electron 44 or later with an ESM main process who wants a file watcher with sensible defaults already in place, and it does not fit a CommonJS app, a project still on Electron below 44, or anyone whose renderer already has a reload strategy.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 16 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two different reactions, decided by what kind of file changed
The behaviour of electron-reloader is easiest to describe as one decision with two branches. When a main process file changes, the app is restarted. When any other file changes, the windows are reloaded.
The distinction between the two categories is the interesting part, and the documentation is precise about it. A main process file is a JavaScript file the main process has loaded. Other files the main process uses, like JSON modules, files read with fs, and worker scripts, are not main process files, so a change to them does not restart the app.
That means a JSON configuration file your code reads at startup, a worker script it spawns, or a template file it loads on demand will not trigger a restart. The windows reload instead, and if your main process cached that configuration at boot, the change will not be picked up until you restart the app by hand. That is a real failure mode in a long session and it follows directly from the definition, not from a bug.
The module is deliberately small. The package.json lists six runtime dependencies: chalk for terminal colours, chokidar for the file watching, date-time for timestamps, electron-is-dev for the production check, and find-up to locate the package directory. There is no bundler, no config file format and no plugin system. The whole package is index.js and index.d.ts at the top level, plus a test directory, with the published files list containing only those two files.
The last push was on 2026-10-07, the same day as the v2.0.0 release, and the version before that was v1.2.3 from 2022-02-22. The licence is MIT, the default branch is main, and the package requires Node 22 or later.
Installing it and wiring it into an ESM entry file
The install is a development dependency, because a reload helper has no business in a production bundle.
npm install --save-dev electron-reloaderThe requirement line is strict and worth reading before anything else: an ESM main process and Electron 44 or later. The package is declared with type set to module and the exports map points at index.js with types from index.d.ts, and the engines field requires Node 22 or later.
The setup in the app entry file, usually named index.js, is three statements.
let reloader;
try {
reloader = await import('electron-reloader');
} catch {}
reloader?.default(import.meta);The import.meta argument is not optional. You have to pass it so the module can find the directory to watch, which is how it locates the package root and turns that into the set of paths handed to chokidar.
The try/catch around the import is there for a specific reason: it stops the app from throwing Cannot find package 'electron-reloader' in production, where the development dependency is not installed. Because it wraps only the import, you still see errors from a wrong setup, so a genuine misconfiguration is not silently swallowed. The optional call on the next line, reloader?.default(import.meta), is the no-op when the import failed.
That arrangement means the production behaviour is a silent no-op rather than a crash, which is the right default for a development helper. It also means a broken setup can look like it works, so the debug option below is worth turning on the first time you wire it up.
The ignore option, and why globs will not work
The ignore option is where the real configuration lives, and it is stricter than most people expect when they first read it.
Its type is an array of strings, regular expressions or functions, and all three are passed straight to chokidar. Two constraints follow from that. Globs are not supported, and a path must match exactly. Paths are relative to the package directory, regular expressions are tested against the absolute path with forward slashes also on Windows, and a function receives the absolute path with forward slashes and should return true to ignore the path.
The Windows detail has a concrete consequence: do not anchor regular expressions with ^, because the absolute path on Windows starts with a drive letter and the forward-slash normalisation means an anchored pattern will not match what you expect. To ignore a directory you use a path like 'src', and to ignore a pattern such as test files you use a regular expression like /\.test\.js$/. A function form receives the absolute path with forward slashes and returns true to ignore it.
Defaults are already sensible, and they apply regardless of what you pass. Files and directories starting with a dot are ignored, .map files are ignored, and node_modules directories are ignored. The ignore option is additive to those, so your entries extend the defaults rather than replacing them.
That default set is worth thinking about for a moment. Because node_modules is ignored by default, editing a dependency in place will not trigger anything, and because dot-prefixed files are ignored, a change to a .env file will not reload the windows either. Both of those are cases where a developer might expect a reload and get nothing. The option is the way to add them back, with the exact-match rule in mind.
watchRenderer and debug, the two switches that change what you see
Two options control the visible behaviour, and they are simple enough to describe completely.
watchRenderer is a boolean and defaults to true. It reloads the windows when a file that is not a main process file changes. Setting it to false can be useful if you use a different reload strategy in the renderer process, like HMR, and that is the documented reason for the option rather than a vague performance argument. If your renderer already has hot module replacement running through webpack, you do not want the windows reloading underneath it.
debug is a boolean and defaults to false. It prints watched paths and when files change, and the documentation says it can be useful to make sure you set it up correctly. That is the first thing to turn on when the reloader appears to do nothing, because it answers the question you cannot answer otherwise, which is which paths are actually being watched.
The default for watchRenderer being true is the one that catches teams out. A window reload throws away renderer state, so a form the user was filling in, a scroll position and any unsaved client-side state are lost. The main process restart path is heavier, it spawns a new process, but it also resets everything. Neither is a state-preserving update, and neither is trying to be.
There are no other options. The API surface is reload(importMeta, options?) with three option keys, ignore, watchRenderer and debug, and the type definition file ships alongside the implementation so a TypeScript project gets the shapes.
The Electron 44 requirement and the TypeScript and Webpack paths
Two integration routes are documented, and both of them come down to the same idea: make the watcher react to compiled output rather than to source.
For TypeScript, compile the main process to ESM, for example with module set to nodenext in tsconfig.json and type set to module in package.json, and put the reload(import.meta) call in the source of the entry file. Then add the source directory to the ignore option, so it only reacts when the compiled output changes.
For Webpack watch mode, the same idea in one line: add the source directory to the ignore option. The dist directory is already watched, so when a source file changes, webpack builds it and outputs it to the dist directory, which the module then detects.
Both routes exist because of a specific problem. Without them, a TypeScript project compiled to JavaScript would have two watchers reacting to the same edit: the compiler writing new output files, and the reloader restarting the app on every intermediate state. Ignoring the source directory leaves the compiled output as the only signal, and the restart happens once, on a file that is complete.
The npm scripts in package.json give a sense of the project's own test setup: start runs electron test/main/index.mjs, and test runs xo and tsd, meaning the linting and the type definition tests are the whole test suite. The dev dependency on Electron is version 44.4.5, which matches the documented minimum.
There is no documentation site and no homepage for this package, so the readme in the repository is the entire documentation. The Related section points at other packages from the same author, including electron-util for general development utilities, electron-debug for adding debug features to an Electron app, and electron-serve for static file serving.
Two failure modes the README warns about, and what to use instead
The documentation is unusually candid about the ways this can annoy you, and both warnings have a concrete cause.
The first is the restart behaviour. The restarted app is a new process. Its output still goes to the terminal, but the electron command exits on the first restart, so the terminal prompt comes back and Ctrl+C does not quit the app. Quit the app normally instead.
That reads like a small annoyance and is actually a workflow trap. After the first main process change, your terminal looks like the app has exited, so the reflex is to press Ctrl+C, and doing so kills the wrong process, or does nothing, and you are left with an app running with no visible terminal. The reason is structural: the original electron process is gone and the new one was spawned by a watcher, so it is not a child of the shell command you ran and Ctrl+C does not reach it.
The second failure mode is worse. If the restarted app fails to load, for example because of a syntax error, it no longer reloads. Quit it and start it again.
This is the important one, because it means a syntax error during development breaks the reload loop itself. The watcher is set up by code that ran successfully once, and a file that no longer parses never reaches the point where the watcher can act on it. Your editor shows the error, but the app is dead and needs a manual restart.
The alternative to this package is not another reloader so much as a different arrangement of the same problem. Vite has an Electron plugin that rebuilds and restarts on change, and webpack's own watch mode plus nodemon covers the main process restart case without a runtime dependency. What electron-reloader gives you that those do not is the distinction between main process files and everything else, handled inside the app itself with no separate watcher process and no configuration of which file belongs to which process. If your project already has a build tool that restarts on change, this module may be redundant, and if your project does not, the two-line setup is hard to argue with, provided you are on Electron 44 with an ESM main process.
Editorial conclusion
electron-reloader fits a developer on Electron 44 or later with an ESM main process who wants a file watcher with sensible defaults already in place, and it does not fit a CommonJS app, a project still on Electron below 44, or anyone whose renderer already has a reload strategy. Before wiring it in, confirm your entry file is ESM, because the module is loaded with a dynamic import inside a try/catch, and set the debug option to true on the first run to check which paths are actually being watched.
Frequently asked questions
What does electron-reloader do when a file changes?
When a main process file changes, the app is restarted, and when any other file changes, the windows are reloaded. A main process file is a JavaScript file the main process has loaded, so JSON modules, files read with fs and worker scripts do not cause a restart.
How do I install and set up electron-reloader?
Run npm install --save-dev electron-reloader, then in your ESM entry file wrap the dynamic import in a try/catch and call reloader?.default(import.meta). Passing import.meta is required so the module can find the directory to watch.
Why does electron-reloader require Electron 44 and an ESM main process?
The install section states that an ESM main process and Electron 44 or later are required. The package is declared with type set to module, requires Node 22 or later, and the module is loaded through a dynamic import in the app entry file.
Can I ignore specific paths in electron-reloader?
The ignore option takes an array of paths, regular expressions or functions passed to chokidar. Globs are not supported and a path must match exactly, relative to the package directory, and the option is additive to the defaults of dot-prefixed files, .map files and node_modules directories.
What happens when the restarted Electron app fails to load?
The documentation states that if the restarted app fails to load, for example because of a syntax error, it no longer reloads, so you need to quit it and start it again. The same section warns that the electron command exits on the first restart, so Ctrl+C does not quit the app and it should be quit normally.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sindresorhus-electron-reloader)Was this useful?