Idiomatic ClojureScript interface to modern React.js
Discuss at #uix on Clojurians Slack. Bug reports, feature requests and PRs are welcome.
There are no versioned releases yet, use deps.edn
to depend on the code via git deps.
{:deps {uix.core {:git/url "https://github.com/roman01la/uix.git"
:deps/root "core"
:sha "{{commit hash}}"}
uix.dom {:git/url "https://github.com/roman01la/uix.git"
:deps/root "dom"
:sha "{{commit hash}}"}
uix.rn {:git/url "https://github.com/roman01la/uix.git"
:deps/root "rn"
:sha "{{commit hash}}"}}}
(require '[uix.core.alpha :as uix])
(require '[uix.dom.alpha :as uix.dom])
(defn button [{:keys [on-click]} text]
[:button.btn {:on-click on-click}
text])
(defn app []
(let [state* (uix/state 0)]
[:<>
[button {:on-click #(swap! state* dec)} "-"]
[:span @state*]
[button {:on-click #(swap! state* inc)} "+"]]))
(uix.dom/render [app] js/root)
-
Build front-end
clojure -A:dev -m figwheel.main -O advanced -bo dev:prod
-
Run server
clojure -A:dev -m uix.server
-
Run front-end recipes in dev
clojure -A:dev:rec-front
-
Run SSR streaming recipe
clojure -A:dev:rec-ssr
[:div#id.class]
or[:#id.class]
[:> js/Component attrs & children]
- interop with JS components[:<> attrs & children]
-React.Fragment
[:# {:fallback element} & children]
-React.Suspense
React Hooks in idiomatic Clojure style
;; state hook
;; (mutable ref type, re-renders component when mutated)
(let [state (uix/state 0)]
(swap! state inc)
@state) ; 1
;; ref hook
;; (mutable ref type, doesn't cause re-renders)
(let [ref (uix/ref 0)]
(swap! ref inc)
@ref) ; 1
;; effect hook
(uix/effect!
(fn []
(prn "after update")
#(prn "before unmount"))
[deps])
;; convenience macro for uix.core/effect!
(uix/with-effect [deps]
(prn "after update")
#(prn "before unmount"))
;; more in uix.core.alpha ns
Injects provided function into attributes transformation stage. Could be used for various side effects, such as processing styles with CSS-in-JS libraries (see uix.recipes.dynamic-styles
).
(uix.core.alpha/add-transform-fn
(fn [attrs]
(my-transform-attrs attrs)))
NOTE: UIx interpreter is already super fast (3x faster than Reagent and only 2x slower than vanilla React). Use pre-compilation ONLY if you are hitting performance problems.
Compiles Hiccup into inlined React elements at compile-time and hoists constant elements so they can be shared across components in different namespaces (for reference see @babel/plugin-transform-react-inline-elements and @babel/plugin-transform-react-constant-elements). Hoisting is enabled with :optimize-constants
compiler option, which is automatically enabled for :optimizations :advanced
.
(uix/html
[:h1 "Title"])
;; emits this
{
$$typeof: Symbol.for("react.element"),
key: null,
ref: null,
props: { children: "Title" },
_owner: null
}
Loading React components on-demand as Closure modules. See code splitting guide and how lazy loading is used in React with Suspense: guide.
(uix/require-lazy '[uix.components :refer [ui-list]])
[:# {:fallback "Loading..."}
(when show?
[ui-list])]
UIx can be used for SSR or usual templating in both JVM and JavaScript runtimes
See an example in uix.recipes.server-rendering
(uix.dom/render-to-string element) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostring
(uix.dom/render-to-static-markup element) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup
;; Streaming HTML
(uix.dom/render-to-stream element {:on-chunk f}) ;; see https://reactjs.org/docs/react-dom-server.html#rendertonodestream
(uix.dom/render-to-static-stream element {:on-chunk f}) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostaticnodestream
SSR works in JavaScript environment via React's serializer using same API.
- Add
ReactDOMServer
into your dependencies (ascljsjs/react-dom-server
or any other way) - Run
(uix.dom/render-to-string element)
- Hiccup interpretation
clojure -A:dev:benchmark:bench-front
- SSR on JVM
clojure -A:dev:benchmark:bench-ssr
react x 23866 ops/s, elapsed 419ms
uix-interpret x 11848 ops/s, elapsed 844ms
reagent-interpret x 4031 ops/s, elapsed 2481ms
lib | test 1 | test 2 | test 3 |
---|---|---|---|
rum | 107.8 µs | 3.6 ms | 7.7 ms |
uix | 120.8 µs | 3.8 ms | 8.1 ms |
uix streaming | 115.7 µs | 3.4 ms | 7.6 ms |
hiccup | 205.7 µs | 6.5 ms | 16.6 ms |
lib | size | gzip |
---|---|---|
rum | 254KB | 70KB |
reagent | 269KB | 74KB |
uix | 234KB | 65KB |
When developing with Figwheel it is recommended to mark root render function with ^:after-load
meta, so Figwheel can update UI tree once the code was re-evaluated.
(ns ^:figwheel-hooks my.ns)
(defn ^:after-load render []
(uix.dom/render [app] js/root))
When inspecting UI tree in React DevTools, filter out memo
components to get cleaner view of components tree.
scripts/test
Note: to ensure you're using the right Node.js version, you can use nvm and run nvm use
once in the directory. Otherwise the Node.js version you use is in the .nvmrc
file. See nvm repo for more documentation.
- Video editor (private)