Hot Module Replacement
Sygnal has built-in HMR support that preserves application state across code changes.
Vite (automatic)
Section titled “Vite (automatic)”If you’re using the Sygnal Vite plugin, HMR is wired automatically. No extra code needed:
import sygnal from 'sygnal/vite'export default defineConfig({ plugins: [sygnal()] })// src/main.js — just run, no HMR boilerplateimport { run } from 'sygnal'import App from './App.jsx'
run(App)The plugin detects the run() call and injects import.meta.hot.accept / dispose at build time.
Vite (manual)
Section titled “Vite (manual)”If you’re not using the plugin, wire HMR yourself:
import { run } from 'sygnal'import RootComponent from './RootComponent.jsx'
const { hmr, dispose } = run(RootComponent)
if (import.meta.hot) { import.meta.hot.accept('./RootComponent.jsx', hmr) import.meta.hot.dispose(dispose)}Webpack Setup
Section titled “Webpack Setup”import { run } from 'sygnal'import RootComponent from './RootComponent'
const { hmr, dispose } = run(RootComponent)
if (module.hot) { module.hot.accept('./RootComponent', hmr) module.hot.dispose(dispose)}How It Works
Section titled “How It Works”- When a file changes, the bundler triggers the
acceptcallback - Sygnal captures the current application state
- The old application instance is disposed
- A new instance is created with the updated code
- The captured state is restored into the new instance
State is preserved across reloads via window.__SYGNAL_HMR_PERSISTED_STATE.
TypeScript HMR
Section titled “TypeScript HMR”For TypeScript projects using manual HMR, you may need to cast the module:
import { run } from 'sygnal'import App from './app'
const { hmr } = run(App)
if (import.meta.hot) { import.meta.hot.accept('./app', (mod) => { hmr((mod as { default?: typeof App })?.default ?? App) })}This is not necessary when using the Vite plugin.