I am a beginner with electron. I am trying to get an .exe file of the electron app. I started with creating a react app with npx create-react-app my-app --template typescript
. I then installed the following packages
cd my-app
npm install @types/electron-devtools-installer electron-devtools-installer electron-is-dev electron-reload
npm install -D concurrently electron electron-builder wait-on cross-env
my react app has an index.tsx file that looks like this, I am not using an index.html until now:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Element2 from "./components/element2";
import Element1 from "./components/element1";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="path2" element={<Element2/>} />
<Route path="path1" element={<Element1 />} />
</Route>
</Routes>
</BrowserRouter>
</React.StrictMode>
);
I made some changement to the package.json which look like this:
{
"name": "translationsapp",
"homepage": ".",
"main": "build/electron/main.js",
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.14",
"@mui/material": "^5.14.14",
"@npm-iav-frontend/iav-frontend-framework": "^11.0.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/electron-devtools-installer": "^2.2.4",
"@types/jest": "^27.5.2",
"@types/react": "^18.2.28",
"@types/react-dom": "^18.2.13",
"electron-devtools-installer": "^3.2.0",
"electron-is-dev": "^2.0.0",
"electron-reload": "^2.0.0-alpha.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"postinstall": "electron-builder install-app-deps",
"electron:dev": "concurrently \"cross-env BROWSER=none npm start\" \"wait-on http://127.0.0.1:3000 && tsc -p electron -w\" \"wait-on http://127.0.0.1:3000 && tsc -p electron && electron .\"",
"electron:build": "npm run build && tsc -p electron && electron-builder"
},
"build": {
"extends": null,
"files": [
"build/**/*"
],
"directories": {
"buildResources": "assets"
}
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@types/node": "^20.8.7",
"concurrently": "^8.2.2",
"cross-env": "^7.0.3",
"electron": "^27.0.1",
"electron-builder": "^24.6.4",
"wait-on": "^7.0.1"
}
}
I also added a folder called electron with two file main.ts
and tsconfig.json
that looks like this:
// main.ts
import { app, BrowserWindow } from 'electron';
import * as path from 'path';
import * as isDev from 'electron-is-dev';
import installExtension, { REACT_DEVELOPER_TOOLS } from "electron-devtools-installer";
let win: BrowserWindow | null = null;
function createWindow() {
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
}
})
console.log(`this is the link:${__dirname}/../index.html`)
if (isDev) {
win.loadURL('http://localhost:3000');
} else {
// 'build/index.html'
win.loadFile(`${__dirname}/../index.html`);
}
win.on('closed', () => win = null);
// Hot Reloading
if (isDev) {
// 'node_modules/.bin/electronPath'
require('electron-reload')(__dirname, {
electron: path.join(__dirname, '..', '..', 'node_modules', '.bin', 'electron'),
forceHardReset: true,
hardResetMethod: 'exit'
});
}
// DevTools
installExtension(REACT_DEVELOPER_TOOLS)
.then((name) => console.log(`Added Extension: ${name}`))
.catch((err) => console.log('An error occurred: ', err));
if (isDev) {
win.webContents.openDevTools();
}
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (win === null) {
createWindow();
}
});
\\tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"strict": true,
"outDir": "../build", // Output transpiled files to build/electron/
"rootDir": "../",
"noEmitOnError": true,
"typeRoots": [
"node_modules/@types"
]
}
}
The final structure of my project looks like this after the configuration and running the build using npm run electron:build
:
my-app/
├── package.json
│
## render process
├── tsconfig.json
├── public/
├── src/
│
## main process
├── electron/
│ ├── main.ts
│ └── tsconfig.json
│
## build output
├── build/
│ ├── index.html
│ ├── static/
│ │ ├── css/
│ │ └── js/
│ │
│ └── electron/
│ └── main.js
│
## distribution packages
└── dist/
├── win-unpacked/
└── translationsapp Setup 0.1.0.exe
└── other files
Running on dev mode using npm run electron:dev
opens the electron app and renders everything like on the web on http://localhost:3000
. Running the build on the other hand win.loadFile(`${__dirname}/../index.html`)
returns a blank UI. The console doesn't show any errors. Launching the .exe file is the same. This is what I get at the end
I don't understand what's the error and most importantly how can I debug this if there were no errors showing. Thanks for your help!!
For routing on a React + Electron app, you have to use <HashRouter>
instead of <BrowserRouter>
:
<HashRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="path2" element={<Element2/>} />
<Route path="path1" element={<Element1 />} />
</Route>
</Routes>
</HashRouter>
Same rule applies for the router supporting the data APIs: createHashRouter
should be used instead of createBrowserRouter
.
Additionally, since your created your app using CRA, you have to add the homepage
property on your package.json
:
"homepage": "./"