Skip to main content

Migrating from Electron

Migrate your Electron application to Krema for smaller bundle sizes and lower memory usage.

Why Migrate?

AspectElectronKrema
Bundle Size~150-200MB~50-80MB
Memory Usage~150-300MB~80-100MB
BackendNode.jsJava
WebViewBundled ChromiumSystem WebView

Architecture Comparison

Electron:
┌─────────────────────────────────────────┐
│ Main Process │ Renderer Process │
│ (Node.js) │ (Chromium) │
│ - IPC │ - HTML/CSS/JS │
│ - Native APIs │ - React/Vue/etc │
├──────────────────┴─────────────────────┤
│ Bundled Chromium (~150MB) │
└─────────────────────────────────────────┘

Krema:
┌─────────────────────────────────────────┐
│ Java Backend │ WebView Frontend │
│ - @KremaCommand │ - HTML/CSS/JS │
│ - FFM APIs │ - React/Vue/etc │
├──────────────────┴─────────────────────┤
│ System WebView (WKWebView/WebView2)│
└─────────────────────────────────────────┘

Project Structure Changes

Electron:

my-electron-app/
├── package.json
├── main.js # Main process
├── preload.js # Preload script
├── renderer/ # Frontend
└── node_modules/

Krema:

my-krema-app/
├── krema.toml # Configuration
├── pom.xml # Maven build
├── package.json # Frontend only
├── src/ # Frontend
├── src-java/ # Backend
└── node_modules/

Step-by-Step Migration

1. Create New Krema Project

krema init my-app --template react
cd my-app

2. Copy Frontend Code

Copy your renderer code to src/:

  • index.html
  • React/Vue/Svelte components
  • Styles and assets

3. Convert IPC Handlers

Electron (main.js):

const { ipcMain } = require('electron');
const fs = require('fs');

ipcMain.handle('read-file', async (event, filePath) => {
return fs.readFileSync(filePath, 'utf-8');
});

ipcMain.handle('write-file', async (event, filePath, content) => {
fs.writeFileSync(filePath, content);
});

ipcMain.handle('get-user', async (event, userId) => {
const user = await database.findUser(userId);
return { id: user.id, name: user.name, email: user.email };
});

Krema (Commands.java):

import build.krema.command.KremaCommand;
import java.nio.file.Files;
import java.nio.file.Path;

public class Commands {

@KremaCommand
public String readFile(String filePath) throws IOException {
return Files.readString(Path.of(filePath));
}

@KremaCommand
public void writeFile(String filePath, String content) throws IOException {
Files.writeString(Path.of(filePath), content);
}

@KremaCommand
public UserInfo getUser(String userId) {
User user = database.findUser(userId);
return new UserInfo(user.getId(), user.getName(), user.getEmail());
}
}

public record UserInfo(String id, String name, String email) {}

4. Update Frontend Calls

Electron:

const content = await window.electronAPI.readFile('/path/to/file');
const user = await window.electronAPI.getUser('123');

Krema:

const content = await window.krema.invoke('readFile', { filePath: '/path/to/file' });
const user = await window.krema.invoke('getUser', { userId: '123' });

5. Convert Events

Electron:

// Main process
mainWindow.webContents.send('file-changed', { path: filePath });

// Renderer
ipcRenderer.on('file-changed', (event, data) => {
console.log('File changed:', data.path);
});

Krema:

// Java backend
events.emit("file-changed", Map.of("path", filePath));
// Frontend
window.krema.on('file-changed', (data) => {
console.log('File changed:', data.path);
});

API Mapping

Dialog APIs

ElectronKrema
dialog.showOpenDialog()dialog:openFile
dialog.showSaveDialog()dialog:saveFile
dialog.showMessageBox()dialog:message

Electron:

const { dialog } = require('electron');

const result = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text', extensions: ['txt'] }]
});

Krema:

const file = await window.krema.invoke('dialog:openFile', {
filters: [{ name: 'Text', extensions: ['txt'] }]
});

Clipboard APIs

ElectronKrema
clipboard.readText()clipboard:readText
clipboard.writeText()clipboard:writeText
clipboard.readImage()clipboard:readImageBase64

Shell APIs

ElectronKrema
shell.openExternal()shell:open
shell.openPath()shell:open

Notification APIs

ElectronKrema
new Notification()notification:show

Configuration Mapping

Electron (electron-builder.json):

{
"appId": "com.example.myapp",
"productName": "My App",
"mac": {
"category": "public.app-category.developer-tools",
"identity": "Developer ID Application: ..."
},
"win": {
"target": "nsis"
}
}

Krema (krema.toml):

[package]
name = "my-app"
identifier = "com.example.myapp"

[bundle]
icon = "icons/icon.icns"

[bundle.macos]
signing_identity = "Developer ID Application: ..."

[bundle.windows]
certificate_path = "certificate.pfx"

Common Gotchas

No Node.js APIs

Krema frontend doesn't have Node.js. Use backend commands for:

  • File system access
  • Child processes
  • Native modules

Async by Default

All Krema commands are async. Always use await:

// Won't work
const result = window.krema.invoke('readFile', { path });

// Correct
const result = await window.krema.invoke('readFile', { path });

Type Conversions

Java types map to JavaScript:

  • Stringstring
  • int/longnumber
  • booleanboolean
  • List<T>array
  • Map<K,V>object
  • record/classobject

Build & Distribution

Electron:

npm run build
electron-builder --mac --win --linux

Krema:

krema build
krema bundle --type dmg # macOS
krema bundle --type exe # Windows
krema bundle --type appimage # Linux