Developing Ionic applications with Clojurescript

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

1. Note

This blog post is part of a series about developing Clojurescript applications using the Ionic framework.

2. What is Ionic?

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

3. Before you begin

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

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

4. Install required software

Make sure you have nodejs, npm and git installed.

Follow the official installation guide to install Ionic.

npm install -g @ionic/cli

5. Create a new Ionic project

After installing prerequisites, you may create a new Ionic project from the command line.

ionic start

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

You can verify that the project is created by starting it and playing around.

cd cs-starter
ionic serve

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

Once we have verified the app works, we can proceed further.

6. Replace JavaScript with Clojurescript

First, get rid of the existing JavaScript (TypeScript) source. Delete everything from the src folder.

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

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

Create the 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 the clj folder. Create a folder, and inside it create a file with our source. The file should be named core.cljs and located in 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 the `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"

7. Testing

Now you need to compile the project using shadow-cljs.

npx shadow-cljs watch app

It takes a while for the code to be initially compiled to JavaScript. From now on, shadow-cljs will watch for modifications to the source code and automatically compile and reload all the changes.

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

Whenever you save files, shadow-cljs will detect changes, compile them, and the browser will reload them.

8. Troubleshooting

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

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

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

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

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

9. 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 can be found in the phase-1 tag here.

10. What's next?

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

So far I have only played with the web part. For me, the next 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 approach taken in this blog post.

Keywords: ionic clojure clojurescript react programming javascript shadow-cljs