最近、この環境を構築してアプリ開発するのが個人的ブームなのでメモとして残しておきます。
Electron × React を構築
React を利用して Electron を簡単に構築するには electron-vite を利用するのが一番楽だと思ってます。
まず環境を構築したいディレクトリに移動し npm で electron-vite をインストールしていきます。
npm create @quick-start/electron@latest
コマンドを入力すると、以下の様に質問されるので回答していきます。
Project name | 任意のプロジェクト名を記入 |
Select a framework | フレームワークを選びます。ここでは React を |
Add TypeScript? | TypeScript を利用するときは Yes にします |
Add Electron updater plugin? | electron-updater を使うときは Yes にします(たぶん) |
Enable Electron download mirror proxy? | Electron をミラーサーバーからDLするときは Yes にします(たぶん) |
Done. Now run:
と表示されたらインストール完了です。
記載されてるとおり、コマンドを順次実行します。まず最初のコマンドは、プロジェクト名のディレクトリへ移動するコマンドです。なおcd
の移動先はプロジェクト名ごとに変わるので注意しましょう!
cd electron-app
続いて必要なパッケージをインストールするコマンドを実行。
npm install
なお、インストール時にパッケージが非推奨と表示されてしまいます…… electron-vite 側が対応してくれるのを望みましょう……(一応 npm outdated
で最新のバージョンがないか調べて package.json
をそのバージョンに書き換えて npm i
で最新版にできますが面倒くさいですね)
そして最後には以下のコマンドを実行することで Electron を起動できます。
npm run dev
Electron が実行されたことを確認したら、一度閉じて終了させます。
Tailwind CSS を導入する
続いて Tailwind CSS を導入します。
先ほどのディレクトリのまま(プロジェクト名のディレクトリ、今回だと./electron-app
)、以下のコマンドで Tailwind CSS をインストールします。
npm install -D tailwindcss
つづいて Tailwind CSS の設定ファイル tailwind.config.js を作成するコマンドを入力します。
npx tailwindcss init
これで./electron-app/tailwind.config.js
が作成されたのでtailwind.config.js
を開きcontent
を以下の様に調整。拡張子はjs
やjsx
だけでもいいけど将来性を考えて設定してます。
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/renderer/src/**/*.{vue,js,ts,jsx,tsx}',
'./src/renderer/index.html',
],
theme: {
extend: {},
},
plugins: [],
}
つづいて./electron-app/src/renderer/src/assets
に移動しinput.css
を作成。CSS の中身は以下の通り。
@tailwind base;
@tailwind components;
@tailwind utilities;
CSS を作成したら./electron-app/src/renderer/src
へ戻りmain.jsx
を開きます。そして1行目のインポートしている CSS のパスを以下の様に変更します。
import './assets/output.css'
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
そして最後にプロジェクトのルート(今回は./electron-app
)へ戻りpackage.json
のscripts
に Tailwind CLI ビルドプロセスを記載して、簡単に CSS をビルドできるようにしておきます。
{
"name": "electron-app",
"version": "1.0.0",
"description": "An Electron application with React",
"main": "./out/main/index.js",
"author": "example.com",
"homepage": "https://electron-vite.org",
"scripts": {
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"start": "electron-vite preview",
"dev": "electron-vite dev",
"build": "electron-vite build",
"tailwindcss": "npx tailwindcss -i ./src/renderer/src/assets/input.css -o ./src/renderer/src/assets/output.css --watch",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
"build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux"
},
"dependencies": {
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"electron-updater": "^6.1.7"
},
……
設定が完了したので実際に CLI ツールを起動して CSS が自動的にビルドされるか確認をしましょう。プロジェクトのルート(今回は./electron-app
)で CLI ツールを起動してみます。
npm run tailwindcss
起動するとDone in 169ms.
など表示されビルドが完了した旨が表示されます。ちなみに Electron 側で Tailwind CSS を利用してないと以下のスクリーンショットのようにwarn
が出ますが、特にエラーとかではないので安心してください!不安であれば./electron-app/src/renderer/src/assets/output.css
が出来てるか確認するのもアリ!
CSS が作成できたのを確認したら Electron を起動します。
npm run dev
すると画面が崩れてますね…… これは今まで割り当たってた CSS が無くなってしまったからです。
ちょっとデザインを元に合わせてみましょう!
デザインを元に合わせてみる
試しに以下のようにコードを弄ってみます。まずは./electron-app/src/renderer/index.html
を以下に書き換えて保存すると……
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Electron</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/>
</head>
<body
class="min-h-screen flex items-center justify-center overflow-hidden bg-cover select-none bg-gray-100 text-gray-600 dark:bg-gray-900 dark:text-gray-400"
style="background-image: url('./src/assets/wavy-lines.svg');"
>
<div id="root" class="flex items-center justify-center flex-col mb-20"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
ちょっと元のデザインに戻ってきたかも!(PCがダークモードじゃない場合は白背景になるかと思います)
つづいて./electron-app/src/renderer/src/App.jsx
を弄ってみます。
import Versions from './components/Versions'
import electronLogo from './assets/electron.svg'
function App() {
const ipcHandle = () => window.electron.ipcRenderer.send('ping')
return (
<>
<img alt="logo" className="mb-5 h-32 w-32" src={electronLogo} />
<div className="text-sm font-semibold mb-2.5">Powered by electron-vite</div>
<div className="text-3xl text-white font-bold py-4">
Build an Electron app with <span className="text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text">React</span>
</div>
<p className="font-semibold">
Please try pressing <code className="py-0.5 px-1.5 rounded-sm text-gray-800 bg-gray-100 dark:bg-gray-600 dark:text-gray-100">F12</code> to open the devTool
</p>
<div className="flex pt-8 flex-wrap justify-start">
<div className="shrink-0 p-1.5">
<a href="https://electron-vite.org/" target="_blank" rel="noreferrer" className="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-full text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700">
Documentation
</a>
</div>
<div className="shrink-0 p-1.5">
<a target="_blank" rel="noreferrer" onClick={ipcHandle} className="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-full text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700">
Send IPC
</a>
</div>
</div>
<Versions></Versions>
</>
)
}
export default App
だいぶ近づいてきたかも?!
最後に./electron-app/src/renderer/src/components/Versions.jsx
を弄ってみます。
import { useState } from 'react'
function Versions() {
const [versions] = useState(window.electron.process.versions)
return (
<ul className="pt-8 inline-flex rounded-md shadow-sm">
<li className="px-4 py-2 text-sm font-medium text-gray-900 bg-white rounded-s-lg hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700">Electron v{versions.electron}</li>
<li className="px-4 py-2 text-sm font-medium text-gray-900 bg-white border-r border-l border-gray-200 hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700">Chromium v{versions.chrome}</li>
<li className="px-4 py-2 text-sm font-medium text-gray-900 bg-white rounded-e-lg hover:bg-gray-100 hover:text-blue-700 dark:bg-gray-800 dark:border-gray-700 dark:text-white dark:hover:text-white dark:hover:bg-gray-700">Node v{versions.node}</li>
</ul>
)
}
export default Versions
ほぼそっくりになったので、これでヨシ!👈😺
Flowbite React を導入する
続いて Tailwind CSS を利用した React の UI コンポーネントライブラリである Flowbite React を導入します。これを使う事で Tailwind CSS を1つずつ記入しなくても簡単に UI を構築することができます!(細かいところを弄るとなると Tailwind CSS の クラス名を把握してないとダメなんですけどね……)
プロジェクト名のディレクトリ、今回だと./electron-app
へ戻り、以下のコマンドで Flowbite React をインストールします。
npm install flowbite flowbite-react
インストールが完了したら Flowbite React が利用出来るようtailwind.config.js
を調整します。
const flowbite = require('flowbite-react/tailwind')
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/renderer/src/**/*.{vue,js,ts,jsx,tsx}',
'./src/renderer/index.html',
flowbite.content(),
],
theme: {
extend: {},
},
plugins: [
flowbite.plugin(),
],
}
保存すると Tailwind CSS が自動的に CSS を構築して Electron が白背景になりました……
デザインを調整してみる
では、最後の作業をしてみましょう!
どうやら Flowbite にはダークモードを切り替えるボタンがあるようです。これを導入してみましょう。
./electron-app/src/renderer/src/App.jsx
を開いて、ダークモードの切り替えボタンを追加してみます。
import { DarkThemeToggle, Flowbite } from 'flowbite-react'
import Versions from './components/Versions'
import electronLogo from './assets/electron.svg'
function App() {
const ipcHandle = () => window.electron.ipcRenderer.send('ping')
return (
<Flowbite>
<img alt="logo" className="mb-5 h-32 w-32" src={electronLogo} />
<div className="text-sm font-semibold mb-2.5">Powered by electron-vite</div>
<div className="text-3xl text-white font-bold py-4">
Build an Electron app with <span className="text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text">React</span>
</div>
<p className="font-semibold">
Please try pressing <code className="py-0.5 px-1.5 rounded-sm text-gray-800 bg-gray-100 dark:bg-gray-600 dark:text-gray-100">F12</code> to open the devTool
</p>
<div className="flex pt-8 flex-wrap justify-start">
<div className="shrink-0 p-1.5">
<a href="https://electron-vite.org/" target="_blank" rel="noreferrer" className="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-full text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700">
Documentation
</a>
</div>
<div className="shrink-0 p-1.5">
<a target="_blank" rel="noreferrer" onClick={ipcHandle} className="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-full text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700">
Send IPC
</a>
</div>
<DarkThemeToggle className="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-full text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700" />
</div>
<Versions></Versions>
</Flowbite>
)
}
export default App
無事、ダークモードの切り替えボタンが出ましたね!
試しにクリックすると……ちゃんとダークモードの切り替わりがされました!Happy!!
さて、ライトモードでは見えにくいテキストがあったり、ボタンが浮いてる気がするので、ちょっとデザインを弄りましょう。
ボタンの className を弄るのが大変!ってときは Flowbite のコンポーネントを利用してみましょう!
試しに./electron-app/src/renderer/src/App.jsx
を開いて、UI を Flowbite のコンポーネントを利用してみます。
import { Button, DarkThemeToggle, Flowbite } from 'flowbite-react'
import Versions from './components/Versions'
import electronLogo from './assets/electron.svg'
function App() {
const ipcHandle = () => window.electron.ipcRenderer.send('ping')
return (
<Flowbite>
<img alt="logo" className="mb-5 h-32 w-32" src={electronLogo} />
<div className="text-sm font-semibold mb-2.5">Powered by electron-vite</div>
<div className="text-3xl text-white font-bold py-4">
Build an Electron app with <span className="text-transparent bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text">React</span>
</div>
<p className="font-semibold">
Please try pressing <code className="py-0.5 px-1.5 rounded-sm text-gray-800 bg-gray-100 dark:bg-gray-600 dark:text-gray-100">F12</code> to open the devTool
</p>
<div className="flex pt-8 flex-wrap justify-start">
<div className="shrink-0 p-1.5">
<Button color="light" href="https://electron-vite.org/" target="_blank" rel="noreferrer" >
Documentation
</Button>
</div>
<div className="shrink-0 p-1.5">
<Button color="light" target="_blank" rel="noreferrer" onClick={ipcHandle} >
Send IPC
</Button>
</div>
<div className="shrink-0 p-1.5">
<DarkThemeToggle className="border border-gray-300 bg-white text-gray-900 focus:ring-4 focus:ring-cyan-300 enabled:hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-600 dark:text-white dark:focus:ring-gray-700 dark:enabled:hover:border-gray-700 dark:enabled:hover:bg-gray-700 rounded-lg" />
</div>
</div>
<Versions></Versions>
</Flowbite>
)
}
export default App
するとどうでしょう?長いclassNameからある程度開放されました!デザインも統一感が出ます!
この調子で、色々と活用できると思うのでお試しあれ~