This is a step-by-step review of how I created this template, so that you understand what it is.
- Use Electron Forge to create the boilerplate and configure webpack
- Application-specific
- New source folders
- Move or remove files from the root of the
src
folder - Reference these new source folders as the entry-points in
package.json
- Insert
createApplication
into thesrc/index.ts
boilerplate - Implement Inter-Process Communication (IPC)
- Do not implement Node.js integration in the renderer process
- Summary
The Boilerplates and CLIs section of the Electron documentation recommends Electron Forge.
The Getting Started section says:
To get started with Electron Forge, we first need to initialize a new project.
npx create-electron-app@latest my-app
I did this with the TypeScript + Webpack template:
npx create-electron-app my-new-app --template=typescript-webpack
As described in the React with TypeScript section:
-
Add
"jsx": "react"
to thecompilerOptions
section of the already-createdtsonfig.json
. -
Add the React modules as run-time dependencies:
npm install react react-dom
-
Add React types as build-time dependencies:
npm install -D @types/react @types/react-dom
-
I also added React's
eslint
plugin:npm install -D eslint-plugin-react-hooks
Adding application-specific code makes start-up slower, so I added lines of code described in Electron's Showing the window gracefully section:
-
Specify
show: false
in the options passed to theBrowserWindow
constructor -
Add the following statement:
mainWindow.once("ready-to-show", () => mainWindow.show());
I edited the boilerplate to add support for application-specific code, as follows.
Add the following subfolders to contain application-specific source code:
src/main/
src/preload/
src/renderer/
src/shared-types/
Delete the following trivial files, which were created by the Electron Forge boilerplate,
or rename them to index.ts
in the corresponding subfolder.
src/preload.ts
src/preload.js
src/renderer.ts
This is because a filename like src/renderer.ts
doesn't coexist well with a new subfolder like src/renderer/index.ts
-- because they both resolve import { foo } from 'renderer'
and so one would hide the other.
Also move the boilerplate index.css
and index.html
into the renderer
folder --
because these too are application-specific, are loaded into the renderer process, and are tightly-coupled with
application-specific renderer source code.
Edit the entryPoints
in package.json
as follows, to say that the entry-points are now located in these subfolders:
"entryPoints": [
{
"html": "./src/renderer/index.html",
"js": "./src/renderer/index.ts",
"name": "main_window",
"preload": {
"js": "./src/preload/index.ts"
}
}
]
You may write application-specific code to run within the main process. To implement this:
-
Write and export a
createApplication
function in the newsrc/main
folderexport function createApplication(webContents: WebContents): void { // TODO }
-
Add a new import statement as follows to the boilerplate source code in
src/index.ts
import { createApplication } from "./main";
-
Add a statement as follows to call the new function, after the
BrowserWindow
is created and before it is loaded.// Create the browser window. const mainWindow = new BrowserWindow({ show: false, webPreferences: { preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, }, }); createApplication(mainWindow.webContents); // and load the index.html of the app. mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
Calling it here ensures that any application-specific APIs are created before the renderer is loaded -- which could be important if the renderer tries to call the IPC API as soon as it loads.
If conversely the application wants to be notified when the renderer has loaded,
it can use the webContents.once("did-finish-load", ...)
event.
You can optionally implement IPC as described in the Electron Inter-Process Communication tutorial.
You can define one or two application-specific APIs:
- Called from main and implemented by the renderer
- Vice versa, i.e. called from the renderer and implemented by main
To do this:
-
Declare the APIs using Typescript type declarations in the
src\shared-types
folders, which should be:- Exported using
export type
- Imported using
import type
by modules in thesrc/main
andsrc/renderer
folders
- Exported using
-
Implement the API:
- In the main process, by creating an object in the
src/main
folder to wrap Electron'sipcMain
object - In the renderer process, by creating an object in the
src/preload
folder to wrap Electron'sipcRenderer
object, and exposing it via thecontextBridge.exposeInMainWorld
API.
- In the main process, by creating an object in the
-
Ensure that the preload script is built and loaded:
-
Add this to the
entryPoints
inpackage.json
:"preload": { "js": "./src/preload.ts" }
-
Verify that the corresponding line already exists in
index.ts
:webPreferences: { preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, },
-
Now you can use these APIs in your application-specific main and renderer code.
The renderer.ts
boilerplate created by the Electron Forge template included this comment:
This file will automatically be loaded by webpack and run in the "renderer" context. To learn more about the differences between the "main" and the "renderer" context in Electron, visit:
https://electronjs.org/docs/latest/tutorial/process-model
By default, Node.js integration in this file is disabled. When enabling Node.js integration in a renderer process, please be aware of potential security implications. You can read more about security risks here:
https://electronjs.org/docs/tutorial/security
To enable Node.js integration in this file, open up
main.js
and enable thenodeIntegration
flag:// Create the browser window. mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, }, });
You can do that -- in which case you may not need the two sections above i.e.:
- Application-specific code in the main process
- IPC between the main process and the renderer
This template does not do so:
- For security -- which may not concern you if you only load local, trusted code into the renderer
- For architectural separation-of-concerns -- to make explicit the API between the UI and the "backend"
Now the src
folder is clean:
- The
src
folder contains onlyindex.ts
plus the new subfolders listed above. - The
index.ts
is the original boilerplate with one extracreateApplication
function call inserted - The entry-points in the
preload
andrenderer
folders are defined in theentryPoints
section ofpackage.json