├── .babelrc
├── assets
└── icon.png
├── src
├── App.css
├── components
│ └── App.js
└── index.js
├── .gitignore
├── README.md
├── webpack.build.config.js
├── webpack.dev.config.js
├── package.json
└── main.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-react", "@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bradtraversy/simple-electron-react/HEAD/assets/icon.png
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | body {
6 | font-family: Arial, Helvetica, sans-serif;
7 | }
8 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const App = () => {
4 | return (
5 |
6 |
React Electron Boilerplate
7 |
This is a simple boilerplate for using React with Electron
8 |
9 | )
10 | }
11 |
12 | export default App
13 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import App from './components/App'
4 | import './App.css'
5 |
6 | // Since we are using HtmlWebpackPlugin WITHOUT a template, we should create our own root node in the body element before rendering into it
7 | let root = document.createElement('div')
8 |
9 | root.id = 'root'
10 | document.body.appendChild(root)
11 |
12 | // Now we can render our application into it
13 | render(, document.getElementById('root'))
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build folder and files #
2 | ##########################
3 | release-builds/
4 |
5 | # Development folders and files #
6 | #################################
7 | .tmp/
8 | dist/
9 | node_modules/
10 | *.compiled.*
11 |
12 | # Folder config file #
13 | ######################
14 | Desktop.ini
15 |
16 | # Folder notes #
17 | ################
18 | _ignore/
19 |
20 | # Log files & folders #
21 | #######################
22 | logs/
23 | *.log
24 | npm-debug.log*
25 | .npm
26 |
27 | # Packages #
28 | ############
29 | # it's better to unpack these files and commit the raw source
30 | # git has its own built in compression methods
31 | *.7z
32 | *.dmg
33 | *.gz
34 | *.iso
35 | *.jar
36 | *.rar
37 | *.tar
38 | *.zip
39 |
40 | # Photoshop & Illustrator files #
41 | #################################
42 | *.ai
43 | *.eps
44 | *.psd
45 |
46 | # Windows & Mac file caches #
47 | #############################
48 | .DS_Store
49 | Thumbs.db
50 | ehthumbs.db
51 |
52 | # Windows shortcuts #
53 | #####################
54 | *.lnk
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Simple Electron React Boilerplate
2 |
3 | This is a simple boilerplate to get up and running with Electron and React. It is a customized version of [Alex Devero's](https://github.com/alexdevero/electron-react-webpack-boilerplate) repo and is used in my Electron course
4 |
5 | ### Install
6 |
7 | #### Clone this repo
8 |
9 | ```
10 | git clone https://github.com/bradtraversy/simple-electron-react.git
11 | ```
12 |
13 | #### Install dependencies
14 |
15 | ```
16 | npm install
17 | ```
18 |
19 | or
20 |
21 | ```
22 | yarn
23 | ```
24 |
25 | ### Usage
26 |
27 | #### Run the app
28 |
29 | ```
30 | npm run start
31 | ```
32 |
33 | or
34 |
35 | ```
36 | yarn start
37 | ```
38 |
39 | #### Build the app (automatic)
40 |
41 | ```
42 | npm run package
43 | ```
44 |
45 | or
46 |
47 | ```
48 | yarn package
49 | ```
50 |
51 | #### Build the app (manual)
52 |
53 | ```
54 | npm run build
55 | ```
56 |
57 | or
58 |
59 | ```
60 | yarn build
61 | ```
62 |
63 | #### Test the app (after `npm run build` || `yarn run build`)
64 |
65 | ```
66 | npm run prod
67 | ```
68 |
69 | ```
70 | yarn prod
71 | ```
72 |
73 | ### Change app title
74 |
75 | Change the app title in the **webpack.build.config.js** and the **webpack.dev.config.js** files
76 |
--------------------------------------------------------------------------------
/webpack.build.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const path = require('path')
3 | const HtmlWebpackPlugin = require('html-webpack-plugin')
4 | const BabiliPlugin = require('babili-webpack-plugin')
5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
6 |
7 | module.exports = {
8 | module: {
9 | rules: [
10 | {
11 | test: /\.css$/,
12 | use: [MiniCssExtractPlugin.loader, 'css-loader'],
13 | },
14 | {
15 | test: /\.jsx?$/,
16 | use: [{ loader: 'babel-loader', query: { compact: false } }],
17 | },
18 | {
19 | test: /\.(jpe?g|png|gif)$/,
20 | use: [{ loader: 'file-loader?name=img/[name]__[hash:base64:5].[ext]' }],
21 | },
22 | {
23 | test: /\.(eot|svg|ttf|woff|woff2)$/,
24 | use: [
25 | { loader: 'file-loader?name=font/[name]__[hash:base64:5].[ext]' },
26 | ],
27 | },
28 | ],
29 | },
30 | target: 'electron-renderer',
31 | plugins: [
32 | new HtmlWebpackPlugin({ title: 'React Electron App' }),
33 | new MiniCssExtractPlugin({
34 | // Options similar to the same options in webpackOptions.output
35 | // both options are optional
36 | filename: 'bundle.css',
37 | chunkFilename: '[id].css',
38 | }),
39 | new webpack.DefinePlugin({
40 | 'process.env.NODE_ENV': JSON.stringify('production'),
41 | }),
42 | new BabiliPlugin(),
43 | ],
44 | stats: {
45 | colors: true,
46 | children: false,
47 | chunks: false,
48 | modules: false,
49 | },
50 | }
51 |
--------------------------------------------------------------------------------
/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const path = require('path')
3 | const HtmlWebpackPlugin = require('html-webpack-plugin')
4 | const { spawn } = require('child_process')
5 |
6 | module.exports = {
7 | module: {
8 | rules: [
9 | {
10 | test: /\.css$/,
11 | use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
12 | },
13 | {
14 | test: /\.jsx?$/,
15 | use: [{ loader: 'babel-loader', query: { compact: false } }],
16 | },
17 | {
18 | test: /\.(jpe?g|png|gif)$/,
19 | use: [{ loader: 'file-loader?name=img/[name]__[hash:base64:5].[ext]' }],
20 | },
21 | {
22 | test: /\.(eot|svg|ttf|woff|woff2)$/,
23 | use: [
24 | { loader: 'file-loader?name=font/[name]__[hash:base64:5].[ext]' },
25 | ],
26 | },
27 | ],
28 | },
29 | target: 'electron-renderer',
30 | plugins: [
31 | new HtmlWebpackPlugin({ title: 'React Electron App' }),
32 | new webpack.DefinePlugin({
33 | 'process.env.NODE_ENV': JSON.stringify('development'),
34 | }),
35 | ],
36 | devtool: 'cheap-source-map',
37 | devServer: {
38 | contentBase: path.resolve(__dirname, 'dist'),
39 | stats: {
40 | colors: true,
41 | chunks: false,
42 | children: false,
43 | },
44 | before() {
45 | spawn('electron', ['.'], {
46 | shell: true,
47 | env: process.env,
48 | stdio: 'inherit',
49 | })
50 | .on('close', (code) => process.exit(0))
51 | .on('error', (spawnError) => console.error(spawnError))
52 | },
53 | },
54 | }
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "simple-electron-react",
3 | "productName": "React Electron App",
4 | "version": "1.0.0",
5 | "description": "Simple boilerplate for building Electron apps with React",
6 | "license": "MIT",
7 | "engines": {
8 | "node": ">=9.0.0",
9 | "npm": ">=5.0.0",
10 | "yarn": ">=1.0.0"
11 | },
12 | "browserslist": [
13 | "last 4 versions"
14 | ],
15 | "main": "main.js",
16 | "scripts": {
17 | "prod": "cross-env NODE_ENV=production webpack --mode production --config webpack.build.config.js && electron --noDevServer .",
18 | "start": "cross-env NODE_ENV=development webpack-dev-server --hot --host 0.0.0.0 --config=./webpack.dev.config.js --mode development",
19 | "build": "cross-env NODE_ENV=production webpack --config webpack.build.config.js --mode production",
20 | "package": "npm run build",
21 | "postpackage": "electron-packager ./ --out=./release-builds"
22 | },
23 | "dependencies": {
24 | "react": "^16.13.1",
25 | "react-dom": "^16.13.1"
26 | },
27 | "devDependencies": {
28 | "@babel/core": "^7.9.6",
29 | "@babel/preset-env": "^7.9.6",
30 | "@babel/preset-react": "^7.9.4",
31 | "babel-loader": "^8.1.0",
32 | "babili-webpack-plugin": "^0.1.2",
33 | "cross-env": "^7.0.2",
34 | "css-loader": "^3.5.3",
35 | "electron": "^15.5.5",
36 | "electron-devtools-installer": "^3.0.0",
37 | "electron-packager": "^14.2.1",
38 | "file-loader": "^6.0.0",
39 | "html-webpack-plugin": "^4.3.0",
40 | "mini-css-extract-plugin": "^0.9.0",
41 | "style-loader": "^1.2.0",
42 | "webpack": "^4.43.0",
43 | "webpack-cli": "^3.3.11",
44 | "webpack-dev-server": "^3.10.3"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const url = require('url')
3 | const { app, BrowserWindow } = require('electron')
4 |
5 | let mainWindow
6 |
7 | let isDev = false
8 |
9 | if (
10 | process.env.NODE_ENV !== undefined &&
11 | process.env.NODE_ENV === 'development'
12 | ) {
13 | isDev = true
14 | }
15 |
16 | function createMainWindow() {
17 | mainWindow = new BrowserWindow({
18 | width: 1100,
19 | height: 800,
20 | show: false,
21 | icon: `${__dirname}/assets/icon.png`,
22 | webPreferences: {
23 | nodeIntegration: true,
24 | },
25 | })
26 |
27 | let indexPath
28 |
29 | if (isDev && process.argv.indexOf('--noDevServer') === -1) {
30 | indexPath = url.format({
31 | protocol: 'http:',
32 | host: 'localhost:8080',
33 | pathname: 'index.html',
34 | slashes: true,
35 | })
36 | } else {
37 | indexPath = url.format({
38 | protocol: 'file:',
39 | pathname: path.join(__dirname, 'dist', 'index.html'),
40 | slashes: true,
41 | })
42 | }
43 |
44 | mainWindow.loadURL(indexPath)
45 |
46 | // Don't show until we are ready and loaded
47 | mainWindow.once('ready-to-show', () => {
48 | mainWindow.show()
49 |
50 | // Open devtools if dev
51 | if (isDev) {
52 | const {
53 | default: installExtension,
54 | REACT_DEVELOPER_TOOLS,
55 | } = require('electron-devtools-installer')
56 |
57 | installExtension(REACT_DEVELOPER_TOOLS).catch((err) =>
58 | console.log('Error loading React DevTools: ', err)
59 | )
60 | mainWindow.webContents.openDevTools()
61 | }
62 | })
63 |
64 | mainWindow.on('closed', () => (mainWindow = null))
65 | }
66 |
67 | app.on('ready', createMainWindow)
68 |
69 | app.on('window-all-closed', () => {
70 | if (process.platform !== 'darwin') {
71 | app.quit()
72 | }
73 | })
74 |
75 | app.on('activate', () => {
76 | if (mainWindow === null) {
77 | createMainWindow()
78 | }
79 | })
80 |
81 | // Stop error
82 | app.allowRendererProcessReuse = true
83 |
--------------------------------------------------------------------------------