In today’s fast-paced world, getting to the market quickly is critical for any business or product. However, reaching your target audience on various devices can be a challenge, as it often requires developing separate codebases for web, desktop, and mobile applications. This can be time-consuming and costly, not to mention the maintenance headaches that come with managing multiple codebases.

Fortunately, there are technologies available that can help bridge this gap by allowing you to write code once and run it on multiple platforms. This concept is known as “Write once, Run anywhere” or WORA, and it has been a holy grail for many developers.

In this blog, we’ll explore how I tackled this challenge using a combination of React, Electron, and Cordova. By leveraging these technologies, I was able to create a single codebase that could serve web, desktop, and mobile devices. We’ll dive into the specifics of how this was accomplished and the benefits that were realized. So, if you’re looking for ways to streamline your development process and make your code more efficient, read on!


Let’s see how it looks like:

WORA!



First Things First!

Environment Setup

1. Text Editor of choice
2. Download & Install Node.js
3. Download & Install Java SDK
    1. Add (JAVA_HOME) path in Environment Variable
4. Download & Install Android Studio
    1. (SDK Platforms) Download & Install SDK of choice (API 33 Tiramisu latest stable)
    2. (SDK Tools) Download & Install Android SDK Command-line Tools 
    3. Add (ANDROID_HOME) path in Environment Variable
    4. Create and Setup Emulator in Device Manager
        1. Phone of choice
	    2. Image of Choice
5. Download Gradle
    1. Unzip in C:\Gradle
6. Install Cordova 
    1. run `npm i -g cordova`


Scaffolding the project

1. Create the cordova project
    1. run `cordova create project-name`
2. Create the react app
    1. run `npx create-react-app project-name --type typescript`
3. Create an electron.js file under public folder


Initial File Structure

1. Move src and public dir to root folder
2. Consolidate package.json from reactproject to root package.json
3. Run npm install cmd in root folder


Updating Meta tags

  1. In public/index.html
    1. Add the ff in the <head>:
       <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
       <meta name="format-detection" content="telephone=no">
       <meta name="msapplication-tap-highlight" content="no">
       <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
      
    2. Add the ff in the <body> to load cordova.js:
       <script src="cordova.js" type="text/javascript"></script>
      


Configure react dom to be loaded after cordova’s ondevice ready event:

  1. Add the ff since src/index.js is the entry point
         import React from 'react';
         import ReactDOM from 'react-dom';
         import './index.css';
         import App from './App';
         import * as serviceWorker from './serviceWorker';
    
         const renderReactDom = () => {
             ReactDOM.render(<App />, document.getElementById('root'));
             };
    
         if (window.cordova) {
             document.addEventListener('deviceready', () => {
                 renderReactDom();
               }, false);
             } else {
               renderReactDom();
             }
    
         // If you want your app to work offline and load faster, you can change
         // unregister() to register() below. Note this comes with some pitfalls.
         // Learn more about service workers: https://bit.ly/CRA-PWA
         serviceWorker.unregister();
    

Important Note: Since HTML file is going to run directly in webview rather than being hosted on a server, assets need to be accessed using relative path:

  • Add homepage property in package.json: "homepage": "./"


Building the project for Cordova

  1. Code from src written in jsx and es6 cannot be directly used as is inside the cordova app. Prod build react app needs to be used to build the cordova app.

    Flow: Develop in React (write react components in the src) > Production Build (Bundled and transpiled code) > Build cordova app (Use the react prod build as the source for cordova build)

    1. Use Craco.js by running npm i -D @craco/craco
      1. Add craco.config.js in the root file to change app build from build to www folder for cordova.
         const path = require('path');
         module.exports = {
         webpack: {
             configure: (webpackConfig, { env, paths }) => {
                 paths.appBuild = webpackConfig.output.path = path.resolve('www');
                 return webpackConfig;
                     }
                 }
             }
        
    2. In package.json, update build script to craco build --config craco.config.js


Setting up Electron

  1. Add the code below in your electron.js file under public folder
             const electron = require("electron");
             const app = electron.app;
             const BrowserWindow = electron.BrowserWindow;
             const path = require("path");
             const isDev = require("electron-is-dev");
    
             let mainWindow;
    
             function createWindow() {
             mainWindow = new BrowserWindow({ width: 900, height: 680 });
                 mainWindow.loadURL(isDev ? "http://localhost:3000": 
                     `file://${path.join(__dirname, "../build/index.html")}`);
    
                 mainWindow.on("closed", () => (mainWindow = null));
             }
    
             app.on("ready", createWindow);
    
             app.on("window-all-closed", () => {
                 if (process.platform !== "darwin") {
                     app.quit();
                 }
             });
    
             app.on("activate", () => {
                 if (mainWindow === null) {
                     createWindow();
                 }
             });
    


Running for Web and Desktop

Since jsx and es6 can run on both react + electron:

  1. Setup start script to run both platform concurrently
    1. Run npm install --save cross-env electron-is-dev
      1. This will enable debug features only during development for electron.
    2. Run npm install concurrently electron electron-builder wait-on
      1. This will enable to run multiple commands concurrently
  2. Update package.json
    1. Add both the ff in scripts
      1. “both”: “concurrently "npm:start" "npm:electron"”,
      2. “electron-build”: “electron-builder”,
      3. “electron”: “electron .”
  3. Run npm run both


Running in Mobile

If there is already a platform existing

  1. run npm run build
  2. run cordova run platform
    1. e.g. this is for android cordova run android

If there is no platform existing, run this first before following the commands above:

  1. cordova platform add android


Benefits

  1. Increased Efficiency
    • With a single codebase for web, desktop, and mobile devices, you can save time and resources by avoiding the need to develop and maintain separate codebases for each platform.
  2. Consistent User Experience
    • By using the same codebase across all platforms, you can ensure a consistent user experience and branding across your entire project. This can help build trust with your users and enhance their overall experience.
  3. Improved Portability
    • Using a single codebase also makes it easier to port your app to new platforms or devices, as you can simply make minor modifications to the existing code rather than starting from scratch.
  4. Better Collaboration
    • When you’re working on a single codebase, it’s easier to collaborate and share knowledge across the entire team, rather than having siloed teams working on different platforms.
  5. Access to Native Features
    • By using Native Wrappers, you can access native features of the device/OS which can enhance your app’s functionality and user experience.
  6. Cross-Platform Debugging
    • You ocan debug your app across all platforms with a single codebase, rather than having to debug each platform separately.
  7. Faster Development Cycles
    • By reducing the amount of platform-specific code you need to write, you can accelerate your development cycles and get your app to market faster.

Overall, using a “Write once, Run anywhere” approach with React, Electron, and Cordova can save time, reduce costs, and improve the user experience across all platforms.


Try it out

You can clone my project react-electron-cordova-boilerplate to get started :)