Bundler Configuration
Vite Plugin (recommended)
Section titled “Vite Plugin (recommended)”The Sygnal Vite plugin handles everything automatically — JSX configuration and HMR with state preservation:
import { defineConfig } from 'vite'import sygnal from 'sygnal/vite'
export default defineConfig({ plugins: [sygnal()],})That’s it. Your entry file just needs run():
import { run } from 'sygnal'import App from './App.jsx'
run(App)The plugin detects the run() call, finds the imported root component, and automatically injects HMR wiring during development. No manual import.meta.hot boilerplate needed.
Plugin Options
Section titled “Plugin Options”sygnal({ disableJsx: false, // Set true to configure JSX yourself disableHmr: false, // Set true to handle HMR manually})How the HMR transform works
Section titled “How the HMR transform works”In dev mode, the plugin transforms your entry file from:
import { run } from 'sygnal'import App from './App.jsx'run(App)Into:
import { run } from 'sygnal'import App from './App.jsx'const __sygnal = run(App)if (import.meta.hot) { import.meta.hot.accept('./App.jsx', __sygnal.hmr) import.meta.hot.dispose(__sygnal.dispose)}If you already have import.meta.hot in your file, the plugin leaves it alone.
Manual Vite Configuration
Section titled “Manual Vite Configuration”If you prefer not to use the plugin, configure JSX manually:
import { defineConfig } from 'vite'
export default defineConfig({ esbuild: { jsx: 'automatic', jsxImportSource: 'sygnal', }})For TypeScript projects, also add to tsconfig.json:
{ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "sygnal" }}And wire HMR yourself — see Hot Module Replacement.
Other Bundlers
Section titled “Other Bundlers”For Webpack, Rollup, or other bundlers that support the automatic JSX transform, configure them with sygnal as the JSX import source. The general pattern is:
// General pattern (varies by bundler){ jsx: 'automatic', // or equivalent setting jsxImportSource: 'sygnal', // or equivalent setting}Classic JSX transform (legacy, still supported)
If your bundler does not support the automatic JSX transform, you can use the classic transform:
export default defineConfig({ esbuild: { jsxInject: `import { jsx, Fragment } from 'sygnal/jsx'`, jsxFactory: 'jsx', jsxFragment: 'Fragment' }})Note: With the classic transform, some minifiers may rename the Fragment function, causing JSX fragments to break. To fix this with Vite, install terser and add:
build: { minify: 'terser', terserOptions: { mangle: { reserved: ['Fragment'] } }}This is not an issue with the automatic transform.
Using Without JSX
Section titled “Using Without JSX”If you prefer not to use JSX, use the h() function from Sygnal (re-exported from @cycle/dom):
import { h } from 'sygnal'
function MyComponent({ state }) { return h('div', [ h('h1', `Hello ${state.name}`), h('button.increment', 'Click me') ])}