├── .babelrc ├── .gitignore ├── src ├── assets │ └── css │ │ └── App.css ├── index.js ├── components │ └── App │ │ └── HelloWorld.js └── containers │ └── App.js ├── config └── helpers.js ├── public └── index.html ├── LICENSE ├── package.json ├── webpack.build.config.js ├── webpack.dev.config.js ├── README.md └── main.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *npm-debug.log 3 | *.DS_Store 4 | dist/ 5 | builds/ 6 | -------------------------------------------------------------------------------- /src/assets/css/App.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-family: helvetica; 3 | font-weight: 200; 4 | } 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './containers/App'; 4 | 5 | // Now we can render our application into it 6 | render( , document.getElementById('app') ); 7 | -------------------------------------------------------------------------------- /config/helpers.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // Helper functions 4 | function root(args) { 5 | args = Array.prototype.slice.call(arguments, 0); 6 | return path.join.apply(path, [__dirname].concat('../', ...args)); 7 | } 8 | 9 | exports.root = root; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Your App Name 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/components/App/HelloWorld.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const HelloWorld = () => ( 4 |
5 |

Hello, Electron!

6 |

I hope you enjoy using basic-electron-react-boilerplate to start your dev off right!

7 |
8 | ); 9 | 10 | export default HelloWorld; -------------------------------------------------------------------------------- /src/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import '../assets/css/App.css'; 4 | 5 | import HelloWorld from '../components/App/HelloWorld'; 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 |
11 | 12 |
13 | ); 14 | } 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Phillip Barbiero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-electron-react-boilerplate", 3 | "version": "0.7.0", 4 | "description": "Minimal and modern react+electron+webpack boilerplate", 5 | "author": "Keith Weaver", 6 | "homepage": "https://github.com/keithweaver/basic-electron-react-boilerplate", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/keithweaver/basic-electron-react-boilerplate.git" 10 | }, 11 | "contributors": [ 12 | "Phillip Barbiero ", 13 | "Keith Weaver ", 14 | "Denys Dovhan ", 15 | "Willy Ovalle " 16 | ], 17 | "license": "MIT", 18 | "main": "main.js", 19 | "scripts": { 20 | "prod": "webpack --config webpack.build.config.js && electron --noDevServer .", 21 | "dev": "webpack-dev-server --hot --host 0.0.0.0 --port 4000 --config=./webpack.dev.config.js", 22 | "build": "webpack --config webpack.build.config.js", 23 | "package": "webpack --config webpack.build.config.js", 24 | "postpackage": "electron-packager ./ --out=./builds" 25 | }, 26 | "devDependencies": { 27 | "babel-core": "^6.24.1", 28 | "babel-loader": "^7.1.2", 29 | "babel-preset-react": "^6.24.1", 30 | "babili-webpack-plugin": "^0.1.2", 31 | "css-loader": "^0.28.1", 32 | "electron": "^1.7.8", 33 | "electron-packager": "^9.1.0", 34 | "extract-text-webpack-plugin": "^3.0.1", 35 | "file-loader": "^1.1.5", 36 | "html-webpack-plugin": "^2.28.0", 37 | "react": "^16.0.0", 38 | "react-dom": "^16.0.0", 39 | "style-loader": "^0.19.0", 40 | "webpack": "^3.6.0", 41 | "webpack-dev-server": "^2.4.5" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /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 ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | 7 | // Config directories 8 | const SRC_DIR = path.resolve(__dirname, 'src'); 9 | const OUTPUT_DIR = path.resolve(__dirname, 'dist'); 10 | 11 | // Any directories you will be adding code/files into, need to be added to this array so webpack will pick them up 12 | const defaultInclude = [SRC_DIR]; 13 | 14 | module.exports = { 15 | entry: SRC_DIR + '/index.js', 16 | output: { 17 | path: OUTPUT_DIR, 18 | publicPath: './', 19 | filename: 'bundle.js' 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.css$/, 25 | use: ExtractTextPlugin.extract({ 26 | fallback: 'style-loader', 27 | use: 'css-loader' 28 | }), 29 | include: defaultInclude 30 | }, 31 | { 32 | test: /\.jsx?$/, 33 | use: [{ loader: 'babel-loader' }], 34 | include: defaultInclude 35 | }, 36 | { 37 | test: /\.(jpe?g|png|gif)$/, 38 | use: [{ loader: 'file-loader?name=img/[name]__[hash:base64:5].[ext]' }], 39 | include: defaultInclude 40 | }, 41 | { 42 | test: /\.(eot|svg|ttf|woff|woff2)$/, 43 | use: [{ loader: 'file-loader?name=font/[name]__[hash:base64:5].[ext]' }], 44 | include: defaultInclude 45 | } 46 | ] 47 | }, 48 | target: 'electron-renderer', 49 | plugins: [ 50 | new HtmlWebpackPlugin(), 51 | new ExtractTextPlugin('bundle.css'), 52 | new webpack.DefinePlugin({ 53 | 'process.env.NODE_ENV': JSON.stringify('production') 54 | }), 55 | new BabiliPlugin() 56 | ], 57 | stats: { 58 | colors: true, 59 | children: false, 60 | chunks: false, 61 | modules: false 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /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 | const helpers = require('./config/helpers'); 6 | 7 | 8 | // Config directories 9 | const SRC_DIR = path.resolve(__dirname, 'src'); 10 | const OUTPUT_DIR = path.resolve(__dirname, 'dist'); 11 | 12 | // Any directories you will be adding code/files into, need to be added to this array so webpack will pick them up 13 | const defaultInclude = [SRC_DIR]; 14 | 15 | module.exports = { 16 | entry: SRC_DIR + '/index.js', 17 | output: { 18 | path: OUTPUT_DIR, 19 | publicPath: '/', 20 | filename: 'bundle.js' 21 | }, 22 | module: { 23 | rules: [ 24 | { 25 | test: /\.css$/, 26 | use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], 27 | include: defaultInclude 28 | }, 29 | { 30 | test: /\.jsx?$/, 31 | use: [{ loader: 'babel-loader' }], 32 | include: defaultInclude 33 | }, 34 | { 35 | test: /\.(jpe?g|png|gif)$/, 36 | use: [{ loader: 'file-loader?name=img/[name]__[hash:base64:5].[ext]' }], 37 | include: defaultInclude 38 | }, 39 | { 40 | test: /\.(eot|svg|ttf|woff|woff2)$/, 41 | use: [{ loader: 'file-loader?name=font/[name]__[hash:base64:5].[ext]' }], 42 | include: defaultInclude 43 | } 44 | ] 45 | }, 46 | target: 'electron-renderer', 47 | plugins: [ 48 | new HtmlWebpackPlugin({ 49 | template: helpers.root('public/index.html'), 50 | inject: 'body' 51 | }), 52 | new webpack.DefinePlugin({ 53 | 'process.env.NODE_ENV': JSON.stringify('development') 54 | }) 55 | ], 56 | devtool: 'cheap-source-map', 57 | devServer: { 58 | contentBase: OUTPUT_DIR, 59 | stats: { 60 | colors: true, 61 | chunks: false, 62 | children: false 63 | }, 64 | setup() { 65 | spawn( 66 | 'electron', 67 | ['.'], 68 | { shell: true, env: process.env, stdio: 'inherit' } 69 | ) 70 | .on('close', code => process.exit(0)) 71 | .on('error', spawnError => console.error(spawnError)); 72 | } 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Modern and Minimal Electron + React Starter Kit 2 | _Electron, React, Webpack -- Modern and up-to-date, with a handful of quality of life features included_ 3 | 4 | I made this starter kit as most boilerplates were either out-of-date, heavy handed, or enforced a structure on me that I just didnt like. 5 | With a very select assortment of modules, this starter kit is designed to get you up and running very quickly, and to let you easily drop in your own structure and tools on top of it. 6 | The basic structure of `src/` is intentionally minimal to make it easier to allow you to put your own twist on how you like things laid out. 7 | 8 | Production builds babel-minify is used, and ES2015/ES6 transpilation is provided -- As modern node and chromium versions support 99%+ of the ES6 feature set, I feel those steps are unnecessary. 9 | 10 | If you like this project, check out [enhanced-electron-react-boilerplate](https://github.com/pbarbiero/enhanced-electron-react-boilerplate) which is this project with my take on additional modules (photon, redux, less, css modules etc) and my personal project structure (based on the redux ducks proposal) I suggest you give it a look if you want less of a minimalistic take on my starter kit. 11 | 12 | ### To get started: 13 | * Run `npm install` 14 | 15 | ##### Development 16 | * Run `npm run dev` to start webpack-dev-server. Electron will launch automatically after compilation. 17 | 18 | ##### Production 19 | _You have two options, an automatic build or two manual steps_ 20 | 21 | ###### One Shot 22 | * Run `npm run package` to have webpack compile your application into `dist/bundle.js` and `dist/index.html`, and then an electron-packager run will be triggered for the current platform/arch, outputting to `builds/` 23 | 24 | ###### Manual 25 | _Recommendation: Update the "postpackage" script call in package.json to specify parameters as you choose and use the `npm run package` command instead of running these steps manually_ 26 | * Run `npm run build` to have webpack compile and output your bundle to `dist/bundle.js` 27 | * Then you can call electron-packager directly with any commands you choose 28 | 29 | If you want to test the production build (In case you think Babili might be breaking something) after running `npm run build` you can then call `npm run prod`. This will cause electron to load off of the `dist/` build instead of looking for the webpack-dev-server instance. Electron will launch automatically after compilation. 30 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Import parts of electron to use 4 | const {app, BrowserWindow} = require('electron'); 5 | const path = require('path') 6 | const url = require('url') 7 | 8 | // Keep a global reference of the window object, if you don't, the window will 9 | // be closed automatically when the JavaScript object is garbage collected. 10 | let mainWindow; 11 | 12 | // Keep a reference for dev mode 13 | let dev = false; 14 | if ( process.defaultApp || /[\\/]electron-prebuilt[\\/]/.test(process.execPath) || /[\\/]electron[\\/]/.test(process.execPath) ) { 15 | dev = true; 16 | } 17 | 18 | function createWindow() { 19 | // Create the browser window. 20 | mainWindow = new BrowserWindow({ 21 | width: 1024, height: 768, show: false 22 | }); 23 | 24 | // and load the index.html of the app. 25 | let indexPath; 26 | if ( dev && process.argv.indexOf('--noDevServer') === -1 ) { 27 | indexPath = url.format({ 28 | protocol: 'http:', 29 | host: 'localhost:4000', 30 | pathname: 'index.html', 31 | slashes: true 32 | }); 33 | } else { 34 | indexPath = url.format({ 35 | protocol: 'file:', 36 | pathname: path.join(__dirname, 'dist', 'index.html'), 37 | slashes: true 38 | }); 39 | } 40 | mainWindow.loadURL( indexPath ); 41 | 42 | // Don't show until we are ready and loaded 43 | mainWindow.once('ready-to-show', () => { 44 | mainWindow.show(); 45 | // Open the DevTools automatically if developing 46 | if ( dev ) { 47 | mainWindow.webContents.openDevTools(); 48 | } 49 | }); 50 | 51 | // Emitted when the window is closed. 52 | mainWindow.on('closed', function() { 53 | // Dereference the window object, usually you would store windows 54 | // in an array if your app supports multi windows, this is the time 55 | // when you should delete the corresponding element. 56 | mainWindow = null; 57 | }); 58 | } 59 | 60 | // This method will be called when Electron has finished 61 | // initialization and is ready to create browser windows. 62 | // Some APIs can only be used after this event occurs. 63 | app.on('ready', createWindow); 64 | 65 | // Quit when all windows are closed. 66 | app.on('window-all-closed', () => { 67 | // On macOS it is common for applications and their menu bar 68 | // to stay active until the user quits explicitly with Cmd + Q 69 | if (process.platform !== 'darwin') { 70 | app.quit(); 71 | } 72 | }); 73 | 74 | app.on('activate', () => { 75 | // On macOS it's common to re-create a window in the app when the 76 | // dock icon is clicked and there are no other windows open. 77 | if (mainWindow === null) { 78 | createWindow(); 79 | } 80 | }); 81 | --------------------------------------------------------------------------------