Developing Ionic applications with Clojurescript

2022-02-14 - Show how to set up an Ionic project to be used with Clojurescript

Ionic is a supposedly cross-platform development framework which allow developing web, mobile and desktop applications. It uses web based tech stack, and is a good candidate to try to use it with Clojurescript.

Before you begin

This post describes a way of developing Ionic Clojurescript application using standard Ionic tooling. While certainly doable, it provides no clear benefits of using Clojure REPL.

If you are iterested in developing Ionic application using Clojurescript tooling and shadow-cljs with all the benefits it brings with it, check the next post about better approach.

Install required software

Make sure you have nodejs, npm and git installed.

Follow the official installation guide to instal Ionic.

npm install -g @ionic/cli

Create a new Ionic project

After installing preerquisites you may create a new Ionic project from commandline.

ionic start

For the purpose of this blog post I used react as a framework of choice and blank template. I named the project cs-starter.

You can verify if the project is created by starting it and playign around.

cd cs-starter
ionic serve

This will start a newly created application on http://localost:8100 You may start and play with it a bit.

Once we verified the app works we can proceed further.

Replace JavaScript with Clojurescript

First get rid of the existing Javascript (Typesrcipt) source. Delete everything from the src folder.

mv src src_
# or outhright delete
# rm -rf src
mkdir src

Now, we'll need to set up our clojurescript build using shadow-cljs.

Create shadow-cljs.end configuration file with the following content.

{:deps  true
 :nrepl {:port 7002}
 :builds
        {:app {:target     :react-native
               :init-fn    cs-starter.core/init
               :output-dir "src/app"
               :devtools   {:autoload   true
                            ;; :after-load steroid.rn.core/reload
                            }}}}

Since we are using deps to manage Clojure dependencies, we'll need to set it up too using deps.edn

{:deps  {org.clojure/clojure       {:mvn/version "1.10.3"}
         org.clojure/clojurescript {:mvn/version "1.11.4"}
         thheller/shadow-cljs      {:mvn/version "2.17.2"}
         reagent/reagent           {:mvn/version "1.1.0" :exclusions [cljsjs/react cljsjs/react-dom]}
         re-frame/re-frame         {:mvn/version "1.2.0"}
         }
 :paths ["clj"]}

Our source code will be located in clj folder. Create a folder, and inside it create a file with our source. File name should be core.cljs and located in the folder clj/cs_starter/ which corresponds to our cs-starter.core package.

(ns cs-starter.core
  (:require react
            [reagent.core :as reagent]
            [reagent.dom :as rdom]
            ["react-dom" :as rd]
            ["@ionic/react" :as ir]
            ["@ionic/react-router" :as irr]
            ["react-router-dom" :as rrd])) 

(defn home []
  [:> ir/IonPage
    [:> ir/IonHeader
      [:> ir/IonToolbar
        [:> ir/IonTitle "Blank"]]]
    [:> ir/IonConten {:fullscreen true}
      [:> ir/IonHeader {:collapse "condense"}
        [:> ir/IonToolbar
          [:> ir/IonTitle {:size "large"} "Blank"]]]]])

(defn app-root []
  [:> ir/IonApp
    [:> irr/IonReactRouter
      [:> ir/IonRouterOutlet
        [:> rrd/Route {:exact true :path "/home"}
          "Home"]
        [:> rrd/Route {:exact true :path "/"}
           "Root"]]]])

(defn init []
  (println "init started")
  (ir/setupIonicReact)
  (rdom/render app-root (.getElementById js/document"root")) 
  (println "init done"))

Now, create an index.js file in `src` folder that will set up CSS imports and hook up our code.

/* Core CSS required for Ionic components to work properly */
import '@ionic/react/css/core.css';

/* Basic CSS for apps built with Ionic */
import '@ionic/react/css/normalize.css';
import '@ionic/react/css/structure.css';
import '@ionic/react/css/typography.css';

/* Optional CSS utils that can be commented out */
import '@ionic/react/css/padding.css';
import '@ionic/react/css/float-elements.css';
import '@ionic/react/css/text-alignment.css';
import '@ionic/react/css/text-transformation.css';
import '@ionic/react/css/flex-utils.css';
import '@ionic/react/css/display.css';

/* Our Compiled Clojurescript application code" */
import "./app/index.js"

Testing

Now you need to compile project using shadow-cljs.

npx shadow-cljs watch app

It takes a while for code to be initially compiled to javascript. From now on, shadow-cljs will watch for modification of the source code and automaticaly compile and reload all the changes.

Now you should be able to start project using ionic serve, go to http://localhost:8100 and see an application home screen.

Whenever you save files, shadow-cljs will detect chahnges, compile it, and browser will relead it.

Troubleshooting

If you see a bunch of errors when you start your aplication, it may be due to the way Ionic Typescript compiler is configured. Since we don't care about Typescript here, we can remove it from the project.

Open tsconfig.json file and change the value of strict from true to false.

Open package.json and find eslintConfig section that looks something like this:

 "eslintConfig": {
  "extends": [
    "react-app",
    "react-app/jest"
  ]
},

Remove the complete section. We don't need it.

Source code

You can find a working demo application source code on Github. Feel free to fork and experiment further.

The result of this tutorial above can be found in phase-1 tag here.

What's next?

Now you are ready to start developing your Ionc application using familiar Clojurescript tools like reagent and re-frame.

So far I have only played with web part. For me nexp step would be to play a bit more with the Ionic support for mobile and progressive applications using their Capacitor framework.

You can also check the second part of the tutorial that shows how to build a todo application in the next post and eliminate some of the pain points of the aproach take in this blog post.

Keywords: ionic clojure clojurescript react programming javascript shadow-cljs