How to integrate ClojureScript with React Native and Expo today, while treating Re-Natal as historical context rather than the default path.
React Native and Expo with ClojureScript is best approached as an integration problem, not as a special separate platform. Expo gives you a current, mainstream React Native framework path. ClojureScript gives you expressive state and component logic. The integration layer should stay small enough that the host mobile ecosystem still feels familiar.
This page keeps Re-Natal in view because it mattered historically, but it should not be the default recommendation for new work. The safer current path is Expo or plain React Native plus shadow-cljs.
The React Native docs now encourage framework-based setups, and Expo’s own docs recommend creating a project with create-expo-app. That is the right starting point because it keeps you aligned with:
Then ClojureScript can be layered in deliberately rather than by replacing the whole setup.
That host-first stance matters because React Native and Expo evolve with the broader JavaScript and mobile ecosystem. Staying close to that ecosystem reduces the risk that a ClojureScript app becomes trapped behind outdated assumptions about project bootstrapping or package management.
shadow-cljsThe shadow-cljs user guide includes dedicated React Native and Expo guidance. That makes it the strongest current bridge because it handles:
A representative build looks like this:
1{:deps true
2 :builds
3 {:app
4 {:target :react-native
5 :init-fn my-app.core/init
6 :output-dir "app"
7 :devtools {:reload-strategy :full}}}}
On the Expo side, the app configuration points the entry point to the generated JavaScript bundle. This is a more current and stable model than expecting one older wrapper tool to create and manage the whole project for you.
The main benefit is not only compilation. It is architectural honesty. Expo or React Native remain responsible for the mobile runtime. shadow-cljs remains responsible for ClojureScript compilation and reload flow. Those roles are easier to keep current than a wrapper that tries to own everything.
The app entry point can stay simple:
1(ns my-app.core
2 (:require [reagent.core :as r]
3 [shadow.expo :as expo]
4 ["react-native" :as rn]))
5
6(defn home-screen []
7 [:> (.-View rn)
8 {:style {:flex 1
9 :justifyContent "center"
10 :alignItems "center"}}
11 [:> (.-Text rn) "Welcome to the app"]])
12
13(defn start
14 {:dev/after-load true}
15 []
16 (expo/render-root (r/as-element [home-screen])))
17
18(defn init []
19 (start))
This is enough to show the modern shape:
Once the app renders, the important architecture questions are familiar:
This is where ClojureScript can help. Reagent-style state and data-first UI logic can make the app easier to reason about, especially when it has:
Expo is often the safer default because it gives the team a stronger mainstream workflow, friendlier device iteration, and a clearer starting point. Plain React Native becomes more attractive when the app needs lower-level native customization or an ecosystem path that Expo does not fit comfortably.
That decision should be made on product and runtime constraints, not on whether the team wants more or less ClojureScript.
Re-Natal was part of an earlier generation of React Native + ClojureScript workflows. That deserves acknowledgment, but not default status.
The strongest current teaching stance is:
shadow-cljsThat keeps the lesson useful even as the host ecosystem evolves.
The old “Figwheel is the answer” story is also too narrow now. In the current model:
shadow-cljs watch provides the ClojureScript build and reload pathThis mixed approach is more accurate and more maintainable than pretending one ClojureScript-specific tool owns the whole mobile debugging story.
The integration is healthiest when:
Boring integration ages better than clever integration.
flowchart LR
A["Expo Project"] --> B["React Native Runtime"]
C["shadow-cljs Build"] --> B
B --> D["Reagent / ClojureScript UI"]
D --> E["State, Navigation, and Domain Logic"]
The key thing to notice is that no single layer is pretending to be everything. Expo, React Native, and ClojureScript each keep a focused role.
shadow-cljs as the modern ClojureScript build and hot-reload tool.