Developing Ionic applications 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
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.