Migrating from Electron to Lynxtron
If you already have an Electron app, you can think of the migration to Lynxtron as: keep as much of the main process and desktop integration as possible, and replace the Chromium Renderer Web UI with Lynx UI.
Lynxtron's main-process APIs mostly follow Electron. LynxWindow, Menu, Tray, Notification, dialog, shell, screen, and similar desktop APIs are close to their Electron counterparts. The part that needs a real redesign is the UI layer: Lynxtron does not load HTML pages and does not render through the DOM. Window content is driven by a Lynx bundle and rendered with Lynx elements, styles, and runtime DSLs.
Split the Migration Scope First
Start by splitting the app by runtime layer instead of replacing files one by one:
API Mapping
Migrate Main-Process Window Creation
A common Electron window setup:
In Lynxtron, the main process still runs in Node.js, but the window type and UI entry change:
Start by carrying over window shape options such as width, height, frame, transparent, titleBarStyle, and trafficLightPosition. For frameless windows, transparent windows, and custom title bars in Lynx UI, see Building a Custom Window.
Rewrite the Renderer UI
Lynxtron does not run the HTML page from the Electron Renderer directly. To migrate UI code, rewrite the DOM structure, Web CSS, and browser event model as Lynx UI.
Events also need to move from React DOM events to Lynx element events. For example, an Electron React button:
After migrating to ReactLynx:
If your Electron app depends heavily on DOM APIs, browser layout behavior, Canvas, WebGL, or Web component libraries, treat UI migration as a separate phase. Keep the main-process services and communication protocol first, then rewrite each window with Lynx UI.
Progressive Migration: Put Existing Web UI in <webview> First
If rewriting the entire Renderer UI at once is too expensive, you can build the window shell with Lynx UI first and put Web UI that has not been migrated yet into <webview>. This lets you migrate the main process, window creation, packaging, preload scripts, and IPC first while keeping parts of the existing Web pages running.
This is useful as a transition strategy:
- Build the window frame, title bar, navigation, global state, and new page entries with Lynx first.
- Temporarily put old pages that rely heavily on the DOM or Web component libraries into
<webview>. - After a page is migrated, replace its
<webview>content with Lynx elements. - Keep newly migrated main-process capabilities behind Lynxtron bridge or preload scripts, instead of expanding the old Web UI's dependency on Electron Renderer APIs.
Keep in mind that content inside <webview> is still Web content. It does not automatically become Lynx UI. This approach is useful for reducing migration risk and splitting the migration into smaller steps, but core desktop UI should still be gradually rewritten with Lynx elements.
Migrate Preload Scripts and Node.js Exposure
In Electron, a preload script often exposes capabilities with contextBridge.exposeInMainWorld():
The Renderer side calls through window.desktop:
In Lynxtron, the preload script can still use Node.js capabilities, but the exposure target becomes NativeModules.nodejs.exposed in Lynx BTS:
The Lynx UI side calls through NativeModules.nodejs.exposed:
Lynxtron preload scripts and Lynx BTS are isolated JS contexts in the same process. exposeInLynxBTS() exposes JS objects that BTS can call directly. These objects can contain functions, closures, and asynchronous APIs that return Promises. For details, see Node.js and Lynx Communication: Use Preload Scripts.
Migrate IPC
If the Electron Renderer calls the main process with ipcRenderer.invoke():
The Lynx UI side uses NativeModules.bridge.call to make the call:
The Node main process listens for the -lynx-invoke event to handle bridge.call calls, and returns the result through event.sendReply:
If the Electron main process pushes events to the Renderer with webContents.send():
Use LynxWindow.sendGlobalEvent() in Lynxtron:
The Lynx UI side receives it through GlobalEventEmitter. For a complete example, see Bidirectional Message Communication Between Node Main Process and Lynx.
Handle Native Modules, Builds, and Packaging
If the Electron project uses Node.js native modules, rebuild them for the Lynxtron runtime:
Use @lynx-js/lynxtron-builder for app packaging. It is based on electron-builder and can continue reading electron-builder.yml, but it replaces the Electron runtime with the Lynxtron runtime that matches the current Lynxtron dependency. A typical package script becomes:
During migration, check the following:
- Native modules that depend on the Electron ABI are rebuilt with
@lynx-js/lynxtron-rebuild. - Renderer code that directly accessed Node.js has moved into the main process or preload scripts.
directories.app,files,extraResources, and similar options inelectron-builder.ymlpoint to the Lynxtron main-process output, Lynx bundle, and required resources.- The Lynx bundle is built correctly and included in the packaged app.
- Frameless windows, transparent windows, drag regions, and system buttons have been rewritten with Lynx UI.
Recommended Migration Order
- Create a fresh Lynxtron project and make sure the minimal window and packaging flow work.
- Move Electron main-process logic such as app lifecycle, menus, tray icons, and dialogs.
- Replace
BrowserWindowwithLynxWindowand load a minimal Lynx bundle. - Migrate preload scripts and expose the Node.js capabilities needed by UI through
NativeModules.nodejs.exposed. - Rewrite Renderer UI window by window, replacing HTML, DOM, and CSS with Lynx elements and styles. If you need a progressive migration path, use
<webview>first for Web pages that have not been migrated yet. - Migrate IPC to
NativeModules.bridge.call/NativeModules.bridge.sendandLynxWindow.sendGlobalEvent(), and listen for-lynx-invoke/-lynx-messageevents on the main-process side. - Rebuild native modules, then use
@lynx-js/lynxtron-builderto verify packaged assets and platform-specific behavior.