Electron is a framework that lets you build desktop applications using the same three technologies you already use on the web: HTML, CSS and JavaScript. It bundles Chromium (for rendering the interface) and Node.js (for the system-level work), so a single codebase can run as a native-feeling app on Windows, macOS and Linux. Visual Studio Code, Slack and Discord are all built this way.
Writing the app is the easy half. The half that usually trips people up is packaging: turning a folder of JavaScript files into an .exe installer, a .dmg disk image and an .AppImage or .deb package. That is what we are doing here. We will build a tiny demo app first, and then compile it for all three platforms with electron-builder.
Getting Started:
You only need Node.js (version 18 or newer) and npm. Check what you have:
bash
node -v
npm -v
Create the project folder and initialize it:
bash
mkdir electron-demo
cd electron-demo
npm init -y
Now install Electron and the packaging tool. Both are development dependencies — they are used to build the app, not shipped as part of it:
bash
npm install --save-dev electron electron-builder
This is the structure we are going to end up with:
An Electron app has two kinds of processes. The main process is Node.js: it creates windows and talks to the operating system. The renderer process is the web page inside the window. They are isolated from each other, and a preload script is the bridge that exposes a controlled set of functions from one side to the other.
main.js
This creates the window and answers one message coming from the interface:
javascript
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 720,
height: 480,
title: 'Electron Demo',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
});
win.loadFile(path.join(__dirname, 'renderer', 'index.html'));
}
// Handler for the message sent by the renderer.
ipcMain.handle('get-info', () => {
return {
appVersion: app.getVersion(),
electron: process.versions.electron,
node: process.versions.node,
chrome: process.versions.chrome,
platform: process.platform,
arch: process.arch
};
});
app.whenReady().then(() => {
createWindow();
// On macOS, re-create the window when the dock icon is clicked.
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// On Windows and Linux, closing every window quits the app.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
preload.js
Instead of giving the web page full access to Node, we expose exactly one function:
electron-builder will happily build without custom icons, but you will get the default Electron logo. Drop three files into build/ and it picks them up automatically:
File
Platform
Requirements
build/icon.ico
Windows
Multi-size ICO, at least 256×256
build/icon.icns
macOS
ICNS, from a 1024×1024 source
build/icon.png
Linux
PNG, 512×512 or 1024×1024
If you only have a PNG, the easiest path is to keep a 1024×1024 build/icon.png; electron-builder generates the missing formats for Linux and Windows from it. For macOS, providing the .icns yourself is the safer option.
Building for Windows:
Run this on a Windows machine:
bash
npm run dist:win
You end up with two artifacts inside dist/:
Electron Demo Setup 1.0.0.exe — an NSIS installer with a wizard, Start Menu entry and uninstaller.
Electron Demo 1.0.0.exe — the portable build, a single executable that runs with no installation.
Because the mac block above lists both architectures, you get Intel and Apple Silicon versions:
Electron Demo-1.0.0.dmg — Intel (x64) disk image.
Electron Demo-1.0.0-arm64.dmg — Apple Silicon disk image.
.zip versions of both, which is the format auto-update uses on macOS.
You can also produce a single binary that runs natively on both chips:
bash
npx electron-builder --mac --universal
Heads-up on macOSAn unsigned app will be blocked by Gatekeeper on somebody else's Mac. For personal use that is fine — right-click the app and chooseOpen. To distribute it, you need an Apple Developer account, a Developer ID certificate, and notarization.
With a certificate installed and an app-specific password created, signing and notarizing is a matter of environment variables plus one flag:
bash
export APPLE_ID="you@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="ABCDE12345"
npm run dist:mac
Other formats are just one more entry in the target list — rpm for Fedora and RHEL, snap for the Snap Store, tar.gz for a plain archive:
bash
npx electron-builder --linux AppImage deb rpm tar.gz
Cross-Compiling:
The command npm run dist:all (electron-builder -mwl) is real, but it only works from the right host. This is the part of the process worth memorizing:
You are on
Windows target
macOS target
Linux target
macOS
Yes (needs Wine)
Yes
Yes
Linux
Yes (needs Wine)
No
Yes
Windows
Yes
No
Partially — use WSL or Docker
The short version: macOS builds require a Mac, because Apple's code-signing and DMG tooling does not exist anywhere else. Everything else can be arranged. On Linux you can build Windows and Linux artifacts inside the official Docker image without installing Wine yourself:
The clean answer to the Mac problem is to let a build server do it. This GitHub Actions workflow runs the same build on three runners in parallel and uploads every artifact:
Push a tag such as v1.0.0 and a few minutes later you have installers for the three platforms sitting in the workflow's artifacts, all built from the same commit.
The dist Folder:
dist/
├─ Electron Demo Setup 1.0.0.exe <- Windows installer
├─ Electron Demo 1.0.0.exe <- Windows portable
├─ Electron Demo-1.0.0.dmg <- macOS Intel
├─ Electron Demo-1.0.0-arm64.dmg <- macOS Apple Silicon
├─ Electron Demo-1.0.0.AppImage <- Linux universal
├─ electron-demo_1.0.0_amd64.deb <- Debian / Ubuntu
├─ latest.yml / latest-mac.yml <- auto-update metadata
└─ win-unpacked/ mac/ linux-unpacked/ <- raw application folders
Expect a big downloadEvery Electron app ships its own copy of Chromium, so even this demo lands somewhere around 70–100 MB per installer. That is normal, and there is no way around it — it is the price of the framework.
Common Problems:
A blank white window. Almost always a wrong path in win.loadFile(). Always build the path with path.join(__dirname, ...), never a relative string.
Works with npm start, fails when packaged. A file is missing from the files array in the build config. Anything the app loads at runtime has to be listed there.
window.api is undefined. The preload path is wrong, or contextIsolation was turned off.
“Cannot find module” after packaging. The package was installed with --save-dev but the app needs it at runtime. Move it to dependencies.
The build hangs on the first run. electron-builder is downloading the Electron binaries and the NSIS/Wine helpers into its cache. Let it finish; later builds are much faster.
Files:
The complete demo — main.js, preload.js, the renderer folder and the full package.json — is available in the repository:
CodeIgniter is an open-sourceweb framework that is used for rapid web development. CodeIgniter follows the MVC (Model-View-Controller) architectural pattern. It is noted for its speed compared to other PHP web frameworks.
Application Flow Chart
The index.php serves as the front controller, initializing the base resources needed to run CodeIgniter.
The Router examines the HTTP request to determine what should be done with it.
If a cache file exists, it is sent directly to the browser, bypassing the normal system execution.
Security. Before the application controller is loaded, the HTTP request and any user submitted data is filtered for security.
The Controller loads the model, core libraries, helpers, and any other resources needed to process the specific request.
The finalized View is rendered then sent to the web browser to be seen. If caching is enabled, the view is cached first so that on subsequent requests it can be served.
CRUD is an acronym for CREATE, READ, UPDATE, DELETE. The four basic functions in any type of programming.
CRUD typically refers to operations performed in a database.
Create -Generate new record/s.
Read – Reads or retrieve record/s.
Update – Modify record/s.
Delete – Destroy or remove record/s.
Before proceeding on this tutorial, you must have an environment already setup to run and install CodeIgniter 4.
Go to this path “application/models/” and create a model named CrudModel.php. This will handle all our query to the database. The model will have this following methods.
protected$useTimestamps = false; // Set true if you use created_at / updated_at fields
protected$returnType = 'object'; // Return results as objects
/*
Get all records
*/
publicfunctiongetAll()
{
return$this->findAll();
}
/*
Get a specific record by ID
*/
publicfunctiongetById($id)
{
return$this->find($id);
}
/*
Insert new record
*/
publicfunctionsaveRecord(array$data)
{
return$this->insert($data);
}
/*
Update record by ID
*/
publicfunctionupdateRecord($id, array$data)
{
return$this->update($id, $data);
}
/*
Delete record by ID
*/
publicfunctiondeleteRecord($id)
{
return$this->delete($id);
}
}
Create View Files
We will now create the view files. The Views are the codes that what the user see and the code that the user can interact. We will create all these files:
Electron is a framework that lets you build desktop applications using the same three technologies you already use on the web: HTML, CSS and JavaScript. It bundles Chromium (for rendering the interface) and Node.js (for the system-level work), so a single codebase can run as a native-feeling app on Windows, macOS and Linux. Visual Studio Code, Slack and Discord are all built this way.
Writing the app is the easy half. The half that usually trips people up is packaging: turning a folder of JavaScript files into an .exe installer, a .dmg disk image and an .AppImage or .deb package. That is what we are doing here. We will build a tiny demo app first, and then compile it for all three platforms with electron-builder.
Getting Started:
You only need Node.js (version 18 or newer) and npm. Check what you have:
bash
node -v
npm -v
Create the project folder and initialize it:
bash
mkdir electron-demo
cd electron-demo
npm init -y
Now install Electron and the packaging tool. Both are development dependencies — they are used to build the app, not shipped as part of it:
bash
npm install --save-dev electron electron-builder
This is the structure we are going to end up with:
An Electron app has two kinds of processes. The main process is Node.js: it creates windows and talks to the operating system. The renderer process is the web page inside the window. They are isolated from each other, and a preload script is the bridge that exposes a controlled set of functions from one side to the other.
main.js
This creates the window and answers one message coming from the interface:
javascript
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 720,
height: 480,
title: 'Electron Demo',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false
}
});
win.loadFile(path.join(__dirname, 'renderer', 'index.html'));
}
// Handler for the message sent by the renderer.
ipcMain.handle('get-info', () => {
return {
appVersion: app.getVersion(),
electron: process.versions.electron,
node: process.versions.node,
chrome: process.versions.chrome,
platform: process.platform,
arch: process.arch
};
});
app.whenReady().then(() => {
createWindow();
// On macOS, re-create the window when the dock icon is clicked.
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// On Windows and Linux, closing every window quits the app.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
preload.js
Instead of giving the web page full access to Node, we expose exactly one function:
electron-builder will happily build without custom icons, but you will get the default Electron logo. Drop three files into build/ and it picks them up automatically:
File
Platform
Requirements
build/icon.ico
Windows
Multi-size ICO, at least 256×256
build/icon.icns
macOS
ICNS, from a 1024×1024 source
build/icon.png
Linux
PNG, 512×512 or 1024×1024
If you only have a PNG, the easiest path is to keep a 1024×1024 build/icon.png; electron-builder generates the missing formats for Linux and Windows from it. For macOS, providing the .icns yourself is the safer option.
Building for Windows:
Run this on a Windows machine:
bash
npm run dist:win
You end up with two artifacts inside dist/:
Electron Demo Setup 1.0.0.exe — an NSIS installer with a wizard, Start Menu entry and uninstaller.
Electron Demo 1.0.0.exe — the portable build, a single executable that runs with no installation.
Because the mac block above lists both architectures, you get Intel and Apple Silicon versions:
Electron Demo-1.0.0.dmg — Intel (x64) disk image.
Electron Demo-1.0.0-arm64.dmg — Apple Silicon disk image.
.zip versions of both, which is the format auto-update uses on macOS.
You can also produce a single binary that runs natively on both chips:
bash
npx electron-builder --mac --universal
Heads-up on macOSAn unsigned app will be blocked by Gatekeeper on somebody else's Mac. For personal use that is fine — right-click the app and chooseOpen. To distribute it, you need an Apple Developer account, a Developer ID certificate, and notarization.
With a certificate installed and an app-specific password created, signing and notarizing is a matter of environment variables plus one flag:
bash
export APPLE_ID="you@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="ABCDE12345"
npm run dist:mac
Other formats are just one more entry in the target list — rpm for Fedora and RHEL, snap for the Snap Store, tar.gz for a plain archive:
bash
npx electron-builder --linux AppImage deb rpm tar.gz
Cross-Compiling:
The command npm run dist:all (electron-builder -mwl) is real, but it only works from the right host. This is the part of the process worth memorizing:
You are on
Windows target
macOS target
Linux target
macOS
Yes (needs Wine)
Yes
Yes
Linux
Yes (needs Wine)
No
Yes
Windows
Yes
No
Partially — use WSL or Docker
The short version: macOS builds require a Mac, because Apple's code-signing and DMG tooling does not exist anywhere else. Everything else can be arranged. On Linux you can build Windows and Linux artifacts inside the official Docker image without installing Wine yourself:
The clean answer to the Mac problem is to let a build server do it. This GitHub Actions workflow runs the same build on three runners in parallel and uploads every artifact:
Push a tag such as v1.0.0 and a few minutes later you have installers for the three platforms sitting in the workflow's artifacts, all built from the same commit.
The dist Folder:
dist/
├─ Electron Demo Setup 1.0.0.exe <- Windows installer
├─ Electron Demo 1.0.0.exe <- Windows portable
├─ Electron Demo-1.0.0.dmg <- macOS Intel
├─ Electron Demo-1.0.0-arm64.dmg <- macOS Apple Silicon
├─ Electron Demo-1.0.0.AppImage <- Linux universal
├─ electron-demo_1.0.0_amd64.deb <- Debian / Ubuntu
├─ latest.yml / latest-mac.yml <- auto-update metadata
└─ win-unpacked/ mac/ linux-unpacked/ <- raw application folders
Expect a big downloadEvery Electron app ships its own copy of Chromium, so even this demo lands somewhere around 70–100 MB per installer. That is normal, and there is no way around it — it is the price of the framework.
Common Problems:
A blank white window. Almost always a wrong path in win.loadFile(). Always build the path with path.join(__dirname, ...), never a relative string.
Works with npm start, fails when packaged. A file is missing from the files array in the build config. Anything the app loads at runtime has to be listed there.
window.api is undefined. The preload path is wrong, or contextIsolation was turned off.
“Cannot find module” after packaging. The package was installed with --save-dev but the app needs it at runtime. Move it to dependencies.
The build hangs on the first run. electron-builder is downloading the Electron binaries and the NSIS/Wine helpers into its cache. Let it finish; later builds are much faster.
Files:
The complete demo — main.js, preload.js, the renderer folder and the full package.json — is available in the repository:
CodeIgniter is an open-sourceweb framework that is used for rapid web development. CodeIgniter follows the MVC (Model-View-Controller) architectural pattern. It is noted for its speed compared to other PHP web frameworks.
Application Flow Chart
The index.php serves as the front controller, initializing the base resources needed to run CodeIgniter.
The Router examines the HTTP request to determine what should be done with it.
If a cache file exists, it is sent directly to the browser, bypassing the normal system execution.
Security. Before the application controller is loaded, the HTTP request and any user submitted data is filtered for security.
The Controller loads the model, core libraries, helpers, and any other resources needed to process the specific request.
The finalized View is rendered then sent to the web browser to be seen. If caching is enabled, the view is cached first so that on subsequent requests it can be served.
CRUD is an acronym for CREATE, READ, UPDATE, DELETE. The four basic functions in any type of programming.
CRUD typically refers to operations performed in a database.
Create -Generate new record/s.
Read – Reads or retrieve record/s.
Update – Modify record/s.
Delete – Destroy or remove record/s.
Before proceeding on this tutorial, you must have an environment already setup to run and install CodeIgniter 4.
Go to this path “application/models/” and create a model named CrudModel.php. This will handle all our query to the database. The model will have this following methods.
protected$useTimestamps = false; // Set true if you use created_at / updated_at fields
protected$returnType = 'object'; // Return results as objects
/*
Get all records
*/
publicfunctiongetAll()
{
return$this->findAll();
}
/*
Get a specific record by ID
*/
publicfunctiongetById($id)
{
return$this->find($id);
}
/*
Insert new record
*/
publicfunctionsaveRecord(array$data)
{
return$this->insert($data);
}
/*
Update record by ID
*/
publicfunctionupdateRecord($id, array$data)
{
return$this->update($id, $data);
}
/*
Delete record by ID
*/
publicfunctiondeleteRecord($id)
{
return$this->delete($id);
}
}
Create View Files
We will now create the view files. The Views are the codes that what the user see and the code that the user can interact. We will create all these files: