Skip to content

Migrating from Electron

Move your Electron app to LightShell — what changes, what stays the same.

LightShell and Electron solve the same problem — building desktop apps with web technology — but with very different approaches. This guide walks through what changes when you migrate, and how to translate Electron concepts to LightShell equivalents.

ElectronLightShellNotes
package.jsonlightshell.jsonApp metadata and config
main.js (main process)Not neededNo separate main process
preload.jsNot neededAPIs available directly on window.lightshell
renderer.jsYour JS files in src/Standard browser JavaScript
BrowserWindowlightshell.json window configDeclarative, not programmatic
ipcMain / ipcRendererAutomaticAll APIs go through IPC transparently
require('electron')window.lightshellGlobal object, no imports
node_modules/Not applicableNo Node.js runtime
electron-builder / electron-forgelightshell buildSingle command, no config file
~150MB app bundle~5MB app bundleSystem webview, no bundled Chromium

Replace your package.json + Electron config with a single lightshell.json:

Before (Electron package.json):

{
"name": "my-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder"
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.0.0"
}
}

After (LightShell lightshell.json):

{
"name": "my-app",
"version": "1.0.0",
"entry": "src/index.html",
"window": {
"title": "My App",
"width": 1200,
"height": 800,
"minWidth": 400,
"minHeight": 300
}
}

In Electron, your UI code lives in renderer files that are loaded by BrowserWindow. In LightShell, move those files into src/:

Electron structure:

my-app/
main.js
preload.js
renderer/
index.html
style.css
app.js
node_modules/
package.json

LightShell structure:

my-app/
lightshell.json
src/
index.html
style.css
app.js

Your HTML, CSS, and frontend JavaScript move over with minimal changes.

Remove all require('electron') calls and window.electronAPI references. Replace them with the window.lightshell equivalents.

// Electron (main.js)
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ width: 800, height: 600 })
win.setTitle('My App')
win.maximize()
// LightShell (your JS)
await lightshell.window.setTitle('My App')
await lightshell.window.maximize()
// Electron (main.js + preload bridge)
const { dialog } = require('electron')
const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text', extensions: ['txt'] }]
})
const filePath = result.filePaths[0]
// LightShell
const filePath = await lightshell.dialog.open({
filters: [{ name: 'Text', extensions: ['txt'] }]
})
// Electron (using Node.js fs)
const fs = require('fs').promises
const content = await fs.readFile('/path/to/file', 'utf-8')
await fs.writeFile('/path/to/file', content)
// LightShell
const content = await lightshell.fs.readFile('/path/to/file')
await lightshell.fs.writeFile('/path/to/file', content)
// Electron
const { clipboard } = require('electron')
clipboard.writeText('Hello')
const text = clipboard.readText()
// LightShell
await lightshell.clipboard.write('Hello')
const text = await lightshell.clipboard.read()
// Electron
const { shell } = require('electron')
shell.openExternal('https://example.com')
shell.openPath('/path/to/file')
// LightShell
await lightshell.shell.open('https://example.com')
await lightshell.shell.open('/path/to/file')
// Electron
new Notification({ title: 'Hello', body: 'World' }).show()
// LightShell
await lightshell.notify.send({ title: 'Hello', body: 'World' })
// Electron
const { app } = require('electron')
const dataPath = app.getPath('userData')
// LightShell
const dataPath = await lightshell.app.dataDir()
// Electron
const os = require('os')
const platform = process.platform
const arch = process.arch
const homeDir = os.homedir()
// LightShell
const platform = await lightshell.system.platform() // "darwin" or "linux"
const arch = await lightshell.system.arch() // "arm64" or "x64"
const homeDir = await lightshell.system.homeDir()

LightShell does not include a Node.js runtime. Remove or replace these patterns:

// Remove these -- they will not work
const path = require('path')
const fs = require('fs')
const http = require('http')
const { exec } = require('child_process')
process.env.MY_VAR
__dirname
__filename
// Replace with LightShell equivalents
// path.join -> string concatenation or template literals
const filePath = `${baseDir}/${fileName}`
// fs -> lightshell.fs
await lightshell.fs.readFile(path)
// http -> lightshell.http
const response = await lightshell.http.fetch('https://api.example.com/data')
// child_process -> lightshell.process
const result = await lightshell.process.exec('git', ['status'])

Replace Electron’s electron-store or raw fs persistence with lightshell.store:

// Electron (with electron-store)
const Store = require('electron-store')
const store = new Store()
store.set('theme', 'dark')
const theme = store.get('theme')
// LightShell
await lightshell.store.set('theme', 'dark')
const theme = await lightshell.store.get('theme')

Note that lightshell.store methods are async, while electron-store methods are synchronous. Add await to all store calls.

Terminal window
cd my-app
lightshell dev

This starts your app with hot reloading. Fix any remaining API mismatches by checking the error messages — LightShell errors include the API method name and a link to the relevant docs.

Terminal window
lightshell build

This produces a native .app bundle on macOS or an AppImage on Linux. No electron-builder config, no signing certificate setup (unless you want code signing), no waiting for Chromium to compile.

Be aware of these Electron features that LightShell does not provide:

Electron FeatureLightShell Status
Node.js in rendererNot available — use lightshell.* APIs
Native Node modules (sharp, better-sqlite3)Not available — use Go backend equivalents
Multi-windowNot in v1
<webview> tag / BrowserViewNot available
Chrome DevTools ProtocolLimited — webview inspector in dev mode only
Chrome extensionsNot available
Custom protocol handlers (file://)lightshell: protocol for bundled assets
session / cookie managementNot available
screen API (display enumeration)Not in v1
powerMonitor / powerSaveBlockerNot available
Windows supportLightShell v2
BenefitDetails
App size~5MB vs ~150MB. No bundled Chromium.
Build speedSeconds, not minutes. No Webpack/Vite step required.
Memory usageSystem webview shares memory with OS. No separate Chromium process.
SimplicityOne config file, one command to build, no main/renderer split.
SecurityPermission system, CSP injection, path traversal protection built in.
AI-friendlyError messages include fix instructions. APIs designed for AI code generation.
No node_modulesZero npm dependencies required to build a LightShell app.

IPC bridge removal. In Electron, you set up contextBridge.exposeInMainWorld in a preload script to expose APIs to the renderer. In LightShell, all APIs are already available on window.lightshell. Remove all IPC bridge code.

Sync vs async. Some Electron APIs are synchronous (e.g., clipboard.readText()). All LightShell APIs are async. Add await where needed.

Node.js module imports. Any require() or import of Node.js built-in modules will fail. Replace with LightShell API calls or remove the functionality.

NPM packages. If you use npm packages for UI (React, Vue, etc.), you can still include them via a CDN <script> tag or a bundled JS file. You just cannot use require() to load them.

Path separators. Electron apps sometimes use path.join() for cross-platform path building. Since LightShell only runs on macOS and Linux (both use /), simple string concatenation works fine.