React Native and Expo with ClojureScript

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.

Start from the Host Ecosystem

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:

  • the current React Native assumptions
  • Expo’s development flow
  • the current device and emulator workflow
  • standard mobile packaging expectations

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.

Add ClojureScript Through shadow-cljs

The shadow-cljs user guide includes dedicated React Native and Expo guidance. That makes it the strongest current bridge because it handles:

  • ClojureScript compilation
  • watch mode
  • React Native targets
  • Expo entry-point integration
  • hot-reload-aware lifecycle hooks

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.

A Small Reagent + Expo Root

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:

  • Expo remains the host framework
  • React Native components still come from the underlying ecosystem
  • ClojureScript owns the view and state logic
  • reload-aware startup is handled in a current toolchain

Once the app renders, the important architecture questions are familiar:

  • how navigation is structured
  • how state is shared or localized
  • how async data is fetched and cached
  • how the app calls native APIs when needed

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:

  • shared state transitions
  • form-heavy workflows
  • offline or sync-sensitive behavior
  • lots of derived UI state

Expo Versus Plain React Native Is a Product Choice

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’s Right Place in the Story

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:

  • explain Re-Natal as historical context
  • do not make it the mandatory setup path
  • teach React Native and Expo through the current mainstream ecosystem
  • add ClojureScript with shadow-cljs

That keeps the lesson useful even as the host ecosystem evolves.

Debugging and Reloading

The old “Figwheel is the answer” story is also too narrow now. In the current model:

  • Expo and React Native provide the host-device workflow
  • shadow-cljs watch provides the ClojureScript build and reload path
  • React Native logs and platform debugging tools remain important

This mixed approach is more accurate and more maintainable than pretending one ClojureScript-specific tool owns the whole mobile debugging story.

Keep the Integration Layer Boring

The integration is healthiest when:

  • the generated JavaScript entry point is obvious
  • native package configuration stays near the host project
  • ClojureScript state and component code stay on the ClojureScript side
  • platform-specific setup is not hidden behind mysterious glue

Boring integration ages better than clever integration.

A Better Integration Model

    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.

Key Takeaways

  • For new work, start from Expo or plain React Native, not a wrapper-first workflow.
  • Use shadow-cljs as the modern ClojureScript build and hot-reload tool.
  • Keep Re-Natal as historical context, not the primary recommendation.
  • Let Expo and React Native own the host mobile environment while ClojureScript owns view and state logic.
  • Treat navigation, state, and native boundaries as the real app-design work after setup.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026