Building Electron Apps faster with ReactJS.

let’s create an electron app with it’s cli.

npx create-electron-app vunr --template=webpack
yarn add -D @babel/core @babel/preset-react babel-loader
yarn add react react-dom

in webpack.rules.js

  {
    test: /\.jsx?$/,
    use: {
      loader: 'babel-loader',
      options: {
        exclude: /node_modules/,
        presets: ['@babel/preset-react']
      }
    }
  },

in src/renderer.js js add this

import './app.jsx';
import './index.css';

create a file src/app.jsx

import React from "react";
import ReactDOM from "react-dom";

const Screen = () => {
  return <div className="screen">screen</div>;
};

ReactDOM.render(<Screen />, document.getElementById("root"));

comment out this line in main.js

mainWindow.webContents.openDevTools();

and also update the mainWindow variable with this, in the same file

  const mainWindow = new BrowserWindow({
    width: 1024,
    height: 768,
    transparent: true,
    titleBarStyle: "hidden",
    backgroundColor: "#48426D;",
    center: true
  });

update your src/index.html file to update the window

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>vunr</title>
  </head>
  <style>
    :not(input):not(textarea),
    :not(input):not(textarea)::after,
    :not(input):not(textarea)::before {
      -webkit-user-select: none;
      user-select: none;
      cursor: default;
    }
    ::-webkit-scrollbar {
      display: none;
    }
    input,
    button,
    textarea,
    :focus {
      outline: none;
    }
    a:hover {
      text-decoration: none;
    }
    .titlebar {
      -webkit-user-select: none;
      -webkit-app-region: drag;
      background: transparent;
      height: 1em;
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
    }
  </style>
  <body>
    <div class="titlebar"></div>
    <div id="root"></div>
  </body>
</html>

and update the index.css file

:root {
  --base-font-size: 18px;
  --base-font-color: #222;
  --background-color: #f2f3f7;
  --button-background-color: #f2f3f7;
  --window-top-position: 5px;
  --border-radius: 5px;
  --button-shadow: -6px -6px 8px rgba(255, 255, 255, 0.9),
    5px 5px 8px rgba(0, 0, 0, 0.07);
  
  --header-height: 15vh;
  --body-height: 85vh;
}

html {
  box-sizing: border-box;
  font-size: var(--base-font-size);
  font-family: "Roboto", sans-serif;
  color: var(--base-font-color);
}

*,
*:before,
*:after {
  box-sizing: inherit;
}
*:focus {outline:none}

html,
body,
#root {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  background-color: #48426D;
}

lets add react three

yarn add three @react-three/fiber

and we can update our app.jsx file

import React, {useRef, useState} from "react";
import ReactDOM from "react-dom";
import { Canvas, useFrame } from "@react-three/fiber";

const Box = (props) => {
  const ref = useRef();
  const [hovered, setHover] = useState(false);
  const [active, setActive] = useState(false);

  useFrame(() => {
    ref.current.rotation.x = ref.current.rotation.y += 0.01;
  });
  
  return (
    <mesh
      {...props}
      ref={ref}
      scale={active ? 1.5 : 1}
      onClick={(e) => setActive(!active)}
      onPointerOver={(e) => setHover(true)}
      onPointerOut={(e) => setHover(false)}
    >
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={hovered ? "#9BC3F0" : "#EEC18D"} />
    </mesh>
  );
}

const Boxes = () => {
  return (
    <Canvas>
      <ambientLight intensity={0.5} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
      <pointLight position={[-10, -10, -10]} />
      <Box position={[-1.2, 0, 0]} />
      <Box position={[1.2, 0, 0]} />
    </Canvas>
  )
}

const Screen = () => {
  return <div className="screen"><Boxes /></div>;
};

ReactDOM.render(<Screen />, document.getElementById("root"));

we need to create icons from an svg so let’s add a dependency for that

yarn add -D icon-gen

lets update our scripts

"scripts": {
  "icons:desktop": "icon-gen -i ./src/assets/icon.svg -o ./dist --icns --ico --favicon sizes=192,512 name=icon-",
  "icons": "[ -f ./dist/icon-512.png ] || yarn icons:desktop",
  "prepare": "mkdir -p dist && yarn icons",
  "start": "electron-forge start",
  "make": "electron-forge make",
  "package:desktop": "electron-forge package",
  "package:desktop:mac": "electron-forge package --platform=darwin --arch=x64",
  "package:desktop:linux": "electron-forge package --platform=linux --arch=x64",
  "package:desktop:windows": "electron-forge package --platform=win32 --arch=x64",
  "make:desktop": "electron-forge make",
  "make:desktop:mac": "electron-forge make --platform=darwin",
  "make:desktop:linux": "electron-forge make --platform=linux --arch=x64",
  "make:desktop:windows": "electron-forge make --platform=win32 --arch=x64",
  "build:desktop": "yarn package:desktop && yarn make:desktop",
  "build:desktop:mac": "yarn package:desktop:mac && yarn make:desktop:mac",
  "build:desktop:linux": "yarn package:desktop:linux && yarn make:desktop:linux",
  "build:desktop:windows": "yarn package:desktop:windows && yarn make:desktop:windows",
  "publish:desktop": "electron-forge publish"
},

create these folders src/assets and src/dist

mkdir src/assets
mkdir src/dist