Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/roman01la/uix
Idiomatic ClojureScript interface to modern React.js
https://github.com/roman01la/uix
clojure clojurescript hiccup hooks react
Last synced: 3 months ago
JSON representation
Idiomatic ClojureScript interface to modern React.js
- Host: GitHub
- URL: https://github.com/roman01la/uix
- Owner: roman01la
- License: epl-2.0
- Archived: true
- Created: 2019-03-10T10:31:50.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-09-26T08:39:05.000Z (about 1 year ago)
- Last Synced: 2024-06-18T10:34:30.465Z (5 months ago)
- Topics: clojure, clojurescript, hiccup, hooks, react
- Language: HTML
- Homepage: https://roman01la.gitbook.io/uix/
- Size: 4.52 MB
- Stars: 430
- Watchers: 27
- Forks: 50
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
> UIx v1 is not actively maintained anymore, consider using its successor v2 at [pitch-io/uix](https://github.com/pitch-io/uix)
_Idiomatic ClojureScript interface to modern React.js_
Discuss at [#uix on Clojurians Slack](http://clojurians.net/). Bug reports, feature requests and PRs are welcome.
UIx v2 is available at https://github.com/pitch-io/uix
[Try it in online REPL](https://roman01la.github.io/uix/)
[Docs and Guides](https://roman01la.gitbook.io/uix/)
[API Documentation](https://roman01la.github.io/uix/docs/)
_If you like what I do, consider supporting my work via donation_
[![](https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-1.svg)](https://www.buymeacoffee.com/romanliutikov)
[![CircleCI](https://circleci.com/gh/roman01la/uix.svg?style=svg)](https://circleci.com/gh/roman01la/uix)
Clojars updates are pushed occasionally, depend via Git deps to get the most recent updates.
[![Clojars Project](https://img.shields.io/clojars/v/uix/core.svg)](https://clojars.org/uix/core)
[![Clojars Project](https://img.shields.io/clojars/v/uix/dom.svg)](https://clojars.org/uix/dom)
[![Clojars Project](https://img.shields.io/clojars/v/uix/rn.svg)](https://clojars.org/uix/rn)```clj
{:deps {uix.core {:git/url "https://github.com/roman01la/uix.git"
:deps/root "core"
:sha "{{replace with commit hash}}"}
uix.dom {:git/url "https://github.com/roman01la/uix.git"
:deps/root "dom"
:sha "{{replace with commit hash}}"}
uix.rn {:git/url "https://github.com/roman01la/uix.git"
:deps/root "rn"
:sha "{{replace with commit hash}}"}}}
``````clj
(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)
```## Recipes
- [State hook](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/state_hook.cljc)
- [Global state and effects](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/global_state.cljc)
- [Dynamic styles](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/dynamic_styles.cljc)
- [Lazy loading](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/lazy_loading.cljc)
- [Server-side rendering](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/server_rendering.clj)
- [Interop between UIx and JS components](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/interop.cljc)
- [Popups](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/popup.cljc)
- [Error Boundaries](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/error_boundary.cljc)
- [React Context](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/context.cljc)- 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`## Features
### Hiccup syntax extension
- `[:div#id.class]` or `[:#id.class]`
- `[:> js/Component attrs & children]` - interop with JS components
- `[:<> attrs & children]` - `React.Fragment`
- `[:# {:fallback element} & children]` - `React.Suspense`### Hooks
React Hooks in idiomatic Clojure style
```clj
;; 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
```### Attributes syntax extension
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`).
```clj
(uix.core.alpha/add-transform-fn
(fn [attrs]
(my-transform-attrs attrs)))
```### Hiccup pre-compilation (advanced)
_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](https://babeljs.io/docs/en/babel-plugin-transform-react-inline-elements) and [@babel/plugin-transform-react-constant-elements](https://babeljs.io/docs/en/babel-plugin-transform-react-constant-elements)). Hoisting is enabled with `:optimize-constants` compiler option, which is automatically enabled for `:optimizations :advanced`.
```clj
(uix/html
[:h1 "Title"]);; emits this
{
$$typeof: Symbol.for("react.element"),
key: null,
ref: null,
props: { children: "Title" },
_owner: null
}
```### Lazy loading components
Loading React components on-demand as Closure modules. See [code splitting](https://clojurescript.org/guides/code-splitting) guide and how lazy loading is used in React with Suspense: [guide](https://reactjs.org/docs/code-splitting.html).
```clj
(uix.core.lazy-loader/require-lazy
'[uix.components :refer [ui-list]])[:# {:fallback "Loading..."}
(when show?
[ui-list])]
```### Server-side rendering
UIx can be used for SSR or usual templating in both JVM and JavaScript runtimes
#### Server-side rendering in JVM
See an example in `uix.recipes.server-rendering`
```clj
(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
```#### Server-side rendering in JS
SSR works in JavaScript environment via React's serializer using same API.
1. Add `ReactDOMServer` into your dependencies (as `cljsjs/react-dom-server` or any other way)
2. Run `(uix.dom/render-to-string element)`## Benchmarks
- Hiccup interpretation `clojure -A:dev:benchmark:bench-front`
- SSR on JVM `clojure -A:dev:benchmark:bench-ssr`### Hiccup interpretation
```
react x 23866 ops/s, elapsed 419ms
uix-interpret x 11848 ops/s, elapsed 844ms
reagent-interpret x 4031 ops/s, elapsed 2481ms
```### SSR on JVM
| 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 |### TodoMVC bundle size
| lib | size | gzip |
| ------- | ----- | ---- |
| rum | 254KB | 70KB |
| reagent | 269KB | 74KB |
| uix | 234KB | 65KB |## Figwheel
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.
```clj
(ns ^:figwheel-hooks my.ns)(defn ^:after-load render []
(uix.dom/render [app] js/root))
```## React DevTools
When inspecting UI tree in React DevTools, filter out `memo` components to get cleaner view of components tree.
## Testing
```
scripts/test
```_Note: to ensure you're using the right Node.js version, you can use [nvm](https://github.com/nvm-sh/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._## Who’s using UIx
- [Zeal (REPL meets Clipboard Manager)](https://github.com/den1k/zeal)
- [Floor Planner](http://floor-planner.surge.sh/)
- [Happy Paw mobile web app](https://github.com/roman01la/happy-paw)
- [ProtonNative app](https://github.com/roman01la/proton-native-cljs)
- [Lumber](https://lumber.dev) - [source](https://github.com/lumberdev/lumber-site)
- [GRID](https://theshopgrid.com)
- [ogre.tools - virtual tabletop](https://ogre.tools/)