NOTE: This is my first Electron.js project β built purely for learning and out of a ensem-exam boredom curiosity. It's not intended for contributions; it's a fun exploration of how web skills can build desktop apps. π§ β¨
- What: A small Electron + React portfolio app (desktop) built to learn Electron and bundling with Webpack + Babel.
- Why: Because itβs wild and awesome that we can package a Chromium browser + Node runtime and build desktop apps the same way we build web pages. π
- Where to look: Key files are
src/index.js(main process),src/preload.js,src/react/*(renderer React app),webpack.config.js, and.babelrc.
- Electron bundles a Chromium browser + Node.js into a desktop app. That means the UI is an HTML/CSS/JS web app (the renderer) running in a Chromium instance, and a separate Node-powered main process manages windows, native integrations and system-level tasks.
- Main process: runs Node and controls application lifecycle, windows, menus, IPC. In this repo that's
src/index.js. - Renderer process: where your React app runs β essentially a web page rendered by the embedded Chromium. The React code lives under
src/react/. - Preload script: a safe place to expose a narrow API from Node to renderer when
contextIsolationis enabled. This repo hassrc/preload.jsexposingipcRenderer. - IPC: Use
ipcMain(main) andipcRenderer(renderer) to send messages between processes. Example in this repo:ipcMain.on('contact-form-submit', ...)insrc/index.jsand renderer submission insrc/react/components/Contact.js.
Important note on security: This repo currently enables
nodeIntegration: trueandcontextIsolation: falsein a few places for simplicity. That's fine for learning, but not recommended for production. A more secure setup usescontextIsolation: true,nodeIntegration: false, and a strictpreload.jsthat exposes only required APIs.
Short answer: Electron already bundles a compatible Chromium for you. You do NOT need to separately install Chromium to run the packaged Electron app.
Why you might want Chromium installed on your machine:
- For debugging or visually comparing behavior in a desktop Chromium installation.
- For running a separate browser instance when developing direct HTML pages outside of Electron.
Install Chromium (optional) β commands (pick the one for your OS):
Linux (Debian/Ubuntu):
sudo apt update
sudo apt install -y chromium-browserLinux (Ubuntu Snap alternative):
sudo snap install chromiumFedora:
sudo dnf install chromiumArch Linux:
sudo pacman -S chromiummacOS (Homebrew):
brew install --cask chromiumWindows (chocolatey):
choco install chromiumAgain: none of the above is required to run the Electron app β Electron bundles Chromium internally.
Prerequisites:
- Node.js (I recommend Node 16+ or whatever your local environment supports).
- npm (or yarn).
Clone + install:
git clone <this-repo-url> my-electron-portfolio
cd my-electron-portfolio
npm installAvailable npm scripts (from package.json):
npm run startβ start Electron and load the built files (uses./node_modules/.bin/electron . --no-sandbox).npm run devβ runwebpack --watchand then start Electron (good for development hot rebuilds).npm run buildβ runwebpackand thenelectron-builderto create platform packages (AppImage / deb configured for Linux inpackage.json).npm run build:webpackβ only runwebpack.
Common dev flow:
- Install dependencies:
npm install - Start development (watch + electron):
npm run dev- Or build and run once:
npm run build:webpack
npm run startIf you want a single-shot packaging step:
npm run buildNote: electron-builder configuration is in the build property of package.json. This repo targets AppImage and deb for Linux. Adjust as needed for macOS / Windows.
src/index.jsβ Electron main process. CreatesBrowserWindow, setspreloadand listens foripcMainevents (e.g., contact form submit).src/preload.jsβ preload script. ExposesipcRendereronwindowfor the renderer. (Simple example:window.ipcRenderer = require('electron').ipcRenderer;)src/react/β React app (renderer):src/react/index.jsβ React entry, mounts<App />to#root.src/react/App.jsβ App component that importsNavbar,Hero,About,Skills,Experience,Education,Contact,Footer,CustomCursor.src/react/components/β components (e.g.,Hero.js,Contact.js,Skills.js,ParallaxBackground.js).
src/build/β generated bywebpack(containsbundle.jsandindex.htmloutput).src/index.jsloadssrc/build/index.htmlin dev/run.assets/β static assets such as resume PDF and images referenced in components.
- High-level architecture
- Single main process (
src/index.js) manages windows and system-level events. - One renderer process (the React app) is loaded by a
BrowserWindowand served fromsrc/build/index.html. The React app is bundled with Webpack. - Communication:
ipcMain(main) <->ipcRenderer(renderer). Example contact form flow:- Renderer sends
contact-form-submitwith form data. - Main logs it and replies with
contact-form-resultafter a simulated timeout.
- Renderer sends
- React approach
- Functional components + hooks (
useState,useEffect). Clean, modern React. - Framer Motion is used for animations (
framer-motion) in a few components (e.g.,CustomCursor, parallax elements). - Tailwind CSS used for styling via
src/react/index.cssandtailwind.config.js. - Assets (images, pdf) are loaded via Webpack asset/resource rules and referenced from components.
- Bundling:
.babelrcandwebpack.config.js
-
.babelrc(present in the repo) uses presets:@babel/preset-envand@babel/preset-react. This allows using modern JS features and JSX. -
webpack.config.jskey points:- Entry:
./src/react/index.js(the React app entrypoint). - Output: writes to
src/build/bundle.js(andindex.htmlviaHtmlWebpackPlugin). The Electron main process loadssrc/build/index.html. - Module rules:
babel-loaderfor.js/.jsx(transpiles JSX and modern JS using.babelrc).style-loader,css-loader,postcss-loaderfor CSS (Tailwind runs via PostCSS).asset/resourcefor images so they are copied intoassets/undersrc/build.
- Plugins:
HtmlWebpackPluginto generateindex.htmlfromsrc/index.html.
- Entry:
Why this setup:
- Webpack + Babel lets us write modern React code and output a single bundle that the Electron renderer can load easily.
- Using
postcss-loader+tailwindcssenables Tailwind utility classes without adding heavy runtime frameworks.
- Packaging
electron-builderis configured viapackage.json -> buildfor packaging.productName,appId, and Linux targets (AppImage,deb) are defined.
- Example security considerations in this repo
nodeIntegration: trueandcontextIsolation: falseare convenient for development (lets renderer require electron directly), but they allow full Node access from the renderer β dangerous for production. PrefercontextIsolation: true+preloadto only expose required functions.
src/index.jsβ main process & IPC handlers.src/preload.jsβ exposesipcRendererto the renderer (simple approach used here).src/react/index.jsβ React renderer entrypoint.src/react/App.jsβ top-level React component.src/react/components/Contact.jsβ contact form that useswindow.require('electron')/ipcRenderer.webpack.config.jsβ bundling rules (babel, css, assets, html plugin)..babelrcβ Babel presets for JSX and modern JS.package.jsonβ scripts andelectron-builderconfiguration.
- If you see a blank window: check that
src/build/index.htmlexists (output ofwebpack). Runnpm run build:webpackto build. - If you modify
preload.jsorsrc/index.js(main process), you must restart Electron to pick up changes. - When using
npm run dev,webpack --watchrebuilds bundles β keep an eye on the terminal for build errors. - If you encounter permission or sandbox issues on Linux, try removing
--no-sandboxfromstartscripts only if you understand the tradeoffs.
This project was built purely out of curiosity and a post-exam boredom urge to learn Electron. It's not a library or production template β it's a personal sandbox. The whole point was: "isn't it interesting that we can build desktop apps just like the way we build webpages?" β so I experimented and documented it here.
If you poke around, you'll find lots of small learning-focused patterns (explicit IPC messages, Framer Motion examples, Tailwind usage) and some shortcuts taken for convenience during learning. If you plan to turn this into a production app later, please follow security best practices first.
- Harden security: set
contextIsolation: true,nodeIntegration: false, and expose only minimal APIs viapreload.js. - Add hot-reload for the main process (tools exist to restart main when files change).
- Add tests for key components.
- Expand
electron-builderconfig for macOS/Windows targets if you want cross-platform distributors.
Thanks for checking this out β building a desktop app with web skills is surprisingly fun. If you want, I can:
- convert the
preload/IPC to a safer pattern, - add a small packaging CI step,
- or make a minimal, production-ready starter based on this learning repo.
Happy hacking! β‘οΈ
β Built for learning & curiosity
I made a couple small changes to the repo to make packaging smoother:
- Added an
authorfield topackage.json(electron-builder requires maintainer info for.deb). Please replace the placeholder email with your real email. - Added a
filesarray to thebuildblock inpackage.jsonsosrc/build(webpack output) and main/preload files are explicitly included in the packaged app.
Below are the step-by-step commands you can run from the project root to produce Linux (AppImage + .deb) and Windows installers. I also include code-signing options for Windows installers.
Important: run these from the project root.
Packaging prerequisites (one time):
# Install electron-builder locally (dev dependency)
npm install --save-dev electron-builder
# Optional helpers for building .deb on Ubuntu
sudo apt update
sudo apt install -y fakeroot dpkg-devBuild + package for Ubuntu (AppImage + .deb)
- Build the renderer (webpack):
npm run build:webpack- Package the app (electron-builder):
# Option A: use your package.json `build` script (runs webpack && electron-builder)
npm run build
# Option B: run electron-builder directly (if you already ran webpack)
npx electron-builder --linux deb --linux AppImage --x64- Test the artifacts (in
dist/by default):
ls -la dist
chmod +x dist/*.AppImage
./dist/*.AppImage
# Install the .deb
sudo dpkg -i dist/*.deb
sudo apt-get install -f # fix dependencies if neededNotes for Linux packaging
- Make sure
src/buildcontainsindex.html,bundle.jsandassets/(webpack emits these when you runnpm run build:webpack). - If assets are missing at runtime, either import images in your components (recommended) or use CopyWebpackPlugin to copy
src/assetsintosrc/build/assets. - If you see warnings about icon or category, add
linux.icon(path to .png/.ico) andlinux.categoryinpackage.jsonbuild config.
Build + package for Windows (recommended: run on Windows or use CI)
Best practice: build on a Windows machine or via CI (GitHub Actions) running on windows-latest.
- Add a Windows icon and update
package.jsonbuild block. Example snippet:
"win": {
"icon": "assets/icon.ico",
"target": ["nsis"]
}- Build on Windows (PowerShell):
npm install
npm run build:webpack
npx electron-builder --win nsis --x64Windows signing (production-ready installers)
Option A β Let electron-builder sign during build (recommended):
- Set these environment variables before running electron-builder:
CSC_LINKβ path or URL to your.p12/.pfxcertificateCSC_KEY_PASSWORDβ the password for the certificate
Example (PowerShell):
$env:CSC_LINK = "C:\path\to\certificate.pfx"
$env:CSC_KEY_PASSWORD = "pfxPassword"
npx electron-builder --win nsis --x64Option B β Sign manually after build with SignTool (Windows SDK):
# sign the installer produced by electron-builder
signtool sign /f "C:\path\to\certificate.pfx" /p "pfxPassword" /tr http://timestamp.digicert.com /td sha256 /fd sha256 "dist\YourInstaller.exe"Notes on signing
- Use a trusted code-signing certificate (PFX) from a CA. For wide distribution, EV (Extended Validation) certs reduce SmartScreen prompts.
- electron-builder can automatically sign if
CSC_LINK/CSC_KEY_PASSWORDare set. In CI, store these as secrets. - Cross-signing from Linux is possible (osslsigncode / Wine) but fragile β prefer Windows or CI.
CI recommendation (GitHub Actions)
Use a Windows runner to build and sign. Example outline:
- Add a GitHub Actions workflow that runs on
windows-latest. - Use repository secrets for
CSC_LINKandCSC_KEY_PASSWORD(or upload PFX as a secure artifact). - Run
npm ci,npm run build:webpack, thennpx electron-builder --win nsis --x64.
If you want, I can add a GitHub Actions workflow file and a win block to package.json, and I can create a short helper to convert a PNG to .ico (or show commands to do that locally).
If you'd like, I can next:
- replace the placeholder email in
package.jsonwith the one you provide, - add
winmetadata (icon + NSIS settings) topackage.json, - create a simple GitHub Actions workflow to build Windows installers automatically.
Tell me which of the above you'd like me to commit next and I'll apply the change.