React apps now run on the desktop with Electron. They also run on the desktop with react-native-desktop, or proton-native. In this simple post, I’ll share my (very small!) experience with proton-native:

First, you need Shadow-CLJS. Because of the npm integration, you can literally just:

npm install proton-native react react-dom create-react-class

And then add [reagent "0.8.1"] to dependencies on shadow-cljs.edn file. Then, configure a :node-script target and start to build things. This means that your shadow-cljs.edn file will be:

{:source-paths ["src" "test"]
 :dependencies [[reagent "0.8.1"]]
 :builds {:desktop {:target :node-script
                    :main demo.app/init
                    :output-to "target/index.js"}}}


Now, you have to create the src/demo/app.cljs with the following contents:

(ns demo.app
  (:require [reagent.core :as r]
            ["react" :as React]
            ["proton-native" :refer [render Window App Button Box]]))

(defonce state (r/atom 0))

(defn- win []
   [:> Box
    [:> Button {:stretchy false :onClick #(prn :Bye)}
     "Click Lol!!"]
    [:> Button {:stretchy false :onClick #(swap! state inc)}
     @state]])

(defonce primary-view (r/atom win))
(defn example []
  [:> App
   [:> Window {:title "Example" :size {:w 300 :h 300} :menuBar false}
    (r/as-element [@primary-view])]])

(defn init [ & args]
  (render (r/as-element [example])))

(defn ^:dev/after-load reload []
  (reset! primary-view win))

There is something that we need to be aware: if you look at example implementation, you’ll see that it derefs the primary-view reagent atom. By doing it, we get auto-reload for free, and its even more impressive considering that it is not supported by proton-native!.