Migrating from Electron
Glyx targets Electron developers directly. This guide maps Electron concepts and APIs to their Glyx equivalents.
You don't need to rewrite your entire app at once. Glyx supports a side-by-side migration — run both apps from the same codebase during transition.
What changes
| Electron | Glyx | Notes |
|---|---|---|
BrowserWindow | glyxWindow | Similar API, no main/renderer split |
ipcMain / ipcRenderer | Not needed | No process boundary |
contextBridge | Not needed | JS runs in the same process as native APIs |
shell.openExternal() | glyxWindow.openExternal(url) (requires shell) | Opens http/https/mailto in the OS browser |
dialog.showOpenDialog() | dialog.openFile() | — |
electron-store | db (SQLite) or @glyx-dev/store | More powerful, SQL |
node-fetch | global fetch() | — |
require('fs') | fs from glyx | — |
Notification | notification | — |
clipboard | clipboard (text only) | Images not yet supported |
nativeTheme | @glyx-dev/design useTheme() | System dark mode detection |
safeStorage | credentials or @glyx-dev/keychain | OS keychain |
autoUpdater | updater | GitHub Releases for full binary updates; a self-hosted JSON manifest works too for JS-only hot patches |
Tray | tray | System tray icon with a native context menu |
There's no app.getPath()-style directory-lookup API today — no JS call
returns the app's data/config/cache directory paths.
The big difference: no process split
Electron has a main process and renderer processes. IPC is required to cross the boundary. Glyx has one process — your React code and native APIs run together:
// Electron — renderer process, needs IPC
window.electron.ipcRenderer.invoke('read-file', path)
// Glyx — direct call, no IPC
const content = await fs.readText(path)This eliminates the entire preload.js / contextBridge pattern.
Window management
// Electron
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ width: 1200, height: 800 })
win.loadFile('index.html')
// Glyx — glyx.config.ts
window: {
width: 1200,
height: 800,
}Multiple windows:
// Glyx
import { glyxWindow } from '@glyx-dev/react'
async function openPreferences() {
const win = await glyxWindow.create({
title: 'Preferences',
width: 600,
height: 500,
})
// Pass initial data — the new window runs the same app bundle from
// scratch, so it decides what to render itself (e.g. from this message
// or from its own routing state), not from a `component` prop.
win.send(JSON.stringify({ screen: 'preferences' }))
}There's no component/loadFile-style prop — create() always opens a
new window running your full app bundle from the top. Use win.send() +
the child window's own message handling (or routing) to decide what it
renders, instead of pointing create() at a specific component.
File dialogs
// Electron
const { dialog } = require('electron')
const result = await dialog.showOpenDialog({ properties: ['openFile'] })
const filePath = result.filePaths[0]
// Glyx
import { dialog } from '@glyx-dev/react'
const filePath = await dialog.openFile()Storing data
// Electron + electron-store
const Store = require('electron-store')
const store = new Store()
store.set('theme', 'dark')
const theme = store.get('theme')
// Glyx — use db directly (both calls are async)
await db.run("INSERT OR REPLACE INTO settings VALUES ('theme', 'dark')")
const rows = await db.query("SELECT value FROM settings WHERE key = 'theme'")
const theme = rows[0]?.valueOr use @glyx-dev/store for a React-hook-based key-value store:
import { createStore } from '@glyx-dev/store'
const useSettings = createStore('settings', { theme: 'dark' })
// In a component:
const { state, set } = useSettings()
set({ theme: 'light' })Menu bar / tray
Tray icons are supported via the tray API (requires the tray capability):
import { tray } from '@glyx-dev/react'
const id = tray.create(iconRgbaBytes, 16, 16, 'My App', [
{ id: 'show', label: 'Show Window' },
{ id: '', separator: true },
{ id: 'quit', label: 'Quit' },
])See the tray API reference for menu items, event polling, and the full example.
There's no glyxWindow.show()/.focus() to bring a window to the
foreground — those methods don't exist. The closest available primitive
is glyxWindow.setMinimized(false), which un-minimizes but doesn't
guarantee raising above other windows on every OS.
React migration
If your Electron app already uses React, migration is mostly:
- Replace
react-domrendering withrender - Replace DOM-dependent libraries with Glyx equivalents
- Replace
window,document,localStoragewith Glyx APIs - Remove IPC code — call native APIs directly
- Replace
electron-*packages withglyxequivalents
// Before (Electron + React DOM)
import ReactDOM from 'react-dom/client'
ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
// After (Glyx)
import { render } from '@glyx-dev/react'
render(<App />)What doesn't migrate
- Browser DevTools extensions — use CDP via
glyx dev --inspectinstead <webview>tag — Glyx has its own<WebView>component (native OS WebView2/WKWebView/WebKitGTK), gated by thewebviewcapability, for the specific case of embedding real web content (an OAuth page, a third-party embed) — it's opt-in and separate from Glyx's own GPU-rendered UI, not a drop-in replacement for<webview>'s full API- Node.js
require()— use the Glyx binding equivalents - Electron-specific npm packages (
electron-updater,electron-builder) — replaced byglyx