I generally use React Native to build mobile applications. But I also like how easy it is to setup multi platform apps using Cordova. With React Native you're limited to react native libraries, however with Cordova you can pretty much use any npm library. Anyway this tutorial is not about comparison. It's how to setup a minimal React & Webpack & Babel application in Cordova so you can build Android, IOS, Windows etc. applications.

The boilerplate that I am going to build now could be found here:

https://github.com/aliustaoglu/cordova-react-starter

Install cordova as global if you haven't already. Then create a new app and add Android as platform. Instead of com.cordova.react you can change it to the Android package name that you would like to create.

npm install -g cordova
cordova create cordova-react-starter com.cordova.react
cd cordova-react-starter
cordova platform add android

Now let's add some dependencies. Of course later on we can add redux and all other libraries that you use with React. But we will keep it minimal.

yarn add react react-dom
yarn add -D @babel/core babel-loader @babel/preset-env @babel/preset-react
yarn add -D webpack webpack-cli

Add cordova webpack plugin:

cordova plugin add cordova-plugin-webpack

Add a new webpack configuration

const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'www'),
    filename: 'index.bundle.js'
  },
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
}

Run prepare and build commands

cordova prepare
cordova build -- --webpackConfig webpack.config.js

This will add index.bundle.js into your www folder. This is a generated bundle file so better if you add this to your gitignore file. And create .babelrc file

{ "presets": ["@babel/preset-env", "@babel/preset-react"] }

Now let's do some cleaning. Remove all bloat from css, index and css files:

First index.html file. Update it as below:

<!DOCTYPE html>

<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; 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">
        <link rel="stylesheet" type="text/css" href="css/index.css">
        <title>Hello React</title>
    </head>
    <body>
        <div class="app">
            
        </div>
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="index.bundle.js"></script>
    </body>
</html>

Note that I've removed "js/index.js" file and instead, placed the bundle that webpack generates. But we need the content of js/index.js. So copy the content of this file and paste into "src/index.js" (the source for the webpack). But we still need to modify some stuff. So, update this file as below and notice how we mount the React App when the device is ready. After this App.js will manage the whole content.

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App';

var app = {
  // Application Constructor
  initialize: function() {
      document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
  },

  // deviceready Event Handler
  //
  // Bind any cordova events here. Common events are:
  // 'pause', 'resume', etc.
  onDeviceReady: function() {
      ReactDOM.render(<App />, document.getElementById('app'))
  },
};

app.initialize();

Clear "www/css/index.css" file too. And here's the App.js content:

import React from 'react'

const App = () => {
  return <div style={{ display: 'flex', justifyContent: 'center' }}>Hello from React</div>
}

export default App

My directory structure is like this:

├── hooks
├── node_modules
├── platforms
│   └── android
├── plugins
│   ├── cordova-plugin-webpack
│   └── cordova-plugin-whitelist
├── src
│   ├── App.js
│   └── index.js
├── www
│   ├── index.bundle.js
│   └── index.html
├── .babelrc
├── config.xml
├── package.json
└── webpack.config.js

Now run below commands to deploy the app.

cordova build -- --webpackConfig webpack.config.js
cordova run android 

To enable livereload run below. So, when you make a change you don't need to create the bundle file and upload apk again.

cordova run -- --livereload

Minimal React application is mounted inside Cordova: