React + Electron Hands-on Project

In this chapter, we will use React + Electron to create a note-taking application. This hands-on project is named ReactElectronNotes, and its features include multi-window support, tray, file operations, and IPC communication. It is a very suitable project for beginners to practice.

If you are not yet familiar with React, you can refer to:React tutorial。

ReactElectronNotesThe project will teach you to build, from scratch, aCross-platform desktop note application, including the following features:

  • React front-end rendering
  • Electron main process management
  • IPC communication (main process ↔ renderer process)
  • Local persistence of note data (electron-store)
  • System tray and menu integration

Project Structure Description

ReactElectronNotes/
│
├── src/                  # React 源码目录
├── build/                # 打包后的 React 静态文件
├── electron-main.js      # Electron 主进程
├── preload.js            # 渲染与主进程通信桥
├── note.html             # 子窗口页面
├── note.js               # 子窗口脚本
├── package.json
└── assets/
    └── icon.png          # 托盘图标

I. Project Feature Overview

Functional module Description
main window Display the list of all notes
child window New Note Input
Local Storage Usageelectron-storeSave data
Tray Minimize to the system tray, click to bring up the main interface
IPC Communication The renderer process interacts with the main process to read and write data.

2. Project Initialization

1. Create a React project

npx create-react-app ReactElectronNotes
cd ReactElectronNotes

2. Install Electron and its dependencies

npm install --save-dev electron electron-builder concurrently wait-on
npm install electron-store

3. Modifypackage.json

Ensure thatscriptsAdd the Electron startup command in:

"main": "electron-main.js",
"scripts": {
  "react-start": "react-scripts start",
  "react-build": "react-scripts build",
  "electron-start": "concurrently \"npm run react-start\" \"wait-on http://localhost:3000 && electron .\"",
  "electron-build": "react-scripts build && electron-builder"
}

3. Create the Electron main process

New Fileelectron-main.js:

Example

const { app, BrowserWindow, Tray, Menu, ipcMain } = require('electron')
const path = require('path')
const Store = require('electron-store')

let mainWindow, noteWindow, tray
const store = new Store()

function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  })

  if (process.env.NODE_ENV === 'development') {
    mainWindow.loadURL('http://localhost:3000')
  } else {
    mainWindow.loadFile(path.join(__dirname, 'build/index.html'))
  }

  mainWindow.on('minimize', (e) => {
    e.preventDefault()
    mainWindow.hide()
  })

  createTray()
}

function createNoteWindow() {
  noteWindow = new BrowserWindow({
    width: 400,
    height: 300,
    parent: mainWindow,
    modal: true,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  })

  noteWindow.loadFile(path.join(__dirname, 'note.html'))
}

function createTray() {
  tray = new Tray(path.join(__dirname, 'assets/icon.png'))
  const menu = Menu.buildFromTemplate([
    { label: Show Window, click: () => mainWindow.show() },
    { label: 'Exit', click: () => app.quit() }
  ])
  tray.setToolTip('ReactElectronNotes')
  tray.setContextMenu(menu)
}

app.whenReady().then(createMainWindow)

ipcMain.handle('get-notes', () => {
  return store.get('notes', [])
})

ipcMain.on('save-note', (event, note) => {
  const notes = store.get('notes', [])
  notes.push(note)
  store.set('notes', notes)
  mainWindow.webContents.send('update-notes', notes)
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit()
})

4. Create Preload Script

Newpreload.js:

Example

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  getNotes: () => ipcRenderer.invoke('get-notes'),
  saveNote: (note) => ipcRenderer.send('save-note', note),
  onUpdateNotes: (callback) => ipcRenderer.on('update-notes', callback)
})

Description:
ThroughcontextBridgeSecurely expose APIs to the renderer process.


V. React Frontend Interface

src/App.js

Example

import React, { useState, useEffect } from 'react'

function App() {
  const [notes, setNotes] = useState([])

  useEffect(() => {
    window.electronAPI.getNotes().then(setNotes)
    window.electronAPI.onUpdateNotes((_event, newNotes) => setNotes(newNotes))
  }, [])

  const openNoteWindow = () => {
    window.open('note.html', New Note, 'width=400,height=300')
  }

  return (
    <div style={{ padding: 20 }}>
      <h1>My Notes</h1>
      <button onClick={openNoteWindow}>New Note</button>
      <ul>
        {notes.map((n, i) => (
          <li key={i}>{n}</li>
        ))}
      </ul>
    </div>
  )
}

export default App

Newnote.htmlandnote.js

note.html

Example

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>New Note</title>
</head>
<body>
  <h2>New Note</h2>
  <textarea id="note"></textarea><br>
  <button id="saveBtn">Save</button>
  <script src="note.js"></script>
</body>
</html>

note.js

Example

const saveBtn = document.getElementById('saveBtn')
saveBtn.addEventListener('click', () => {
  const note = document.getElementById('note').value
  if (note.trim()) {
    window.electronAPI.saveNote(note)
    window.close()
  }
})

6. Running and Packaging

Start Development Environment

npm run electron-start

After opening, the React main window will appear. You can click "New Note" to pop up an input window.

Package and Publish

npm run electron-build

Will generate cross-platform executable files (.exe、.app、.AppImageetc.).


7. Extension Directions

Features Approach
Markdown support Usagereact-markdownRender Content
Data Search React Side Viafilter()Implement Local Search
Auto Save Listen to input fieldonChangeAuto Save
cloud sync Integrate Firebase or a self-built REST API
Shortcuts UsageglobalShortcutRegister Ctrl+N to Create New Note
other extensions