{"id":13682301,"url":"https://github.com/roman01la/uix","last_synced_at":"2025-04-30T07:31:21.595Z","repository":{"id":37412952,"uuid":"174808612","full_name":"roman01la/uix","owner":"roman01la","description":"Idiomatic ClojureScript interface to modern React.js","archived":true,"fork":false,"pushed_at":"2023-09-26T08:39:05.000Z","size":4741,"stargazers_count":431,"open_issues_count":1,"forks_count":50,"subscribers_count":27,"default_branch":"master","last_synced_at":"2024-10-02T15:16:02.982Z","etag":null,"topics":["clojure","clojurescript","hiccup","hooks","react"],"latest_commit_sha":null,"homepage":"https://roman01la.gitbook.io/uix/","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/roman01la.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"roman01la","patreon":"roman01la"}},"created_at":"2019-03-10T10:31:50.000Z","updated_at":"2024-08-02T21:44:56.000Z","dependencies_parsed_at":"2024-08-02T13:21:53.986Z","dependency_job_id":"d6140208-edd8-4f71-b177-780ae135cc0b","html_url":"https://github.com/roman01la/uix","commit_stats":{"total_commits":402,"total_committers":15,"mean_commits":26.8,"dds":0.08955223880597019,"last_synced_commit":"843abbe4c4b331fbb0d7d197ecd038b466b48e43"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roman01la%2Fuix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roman01la%2Fuix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roman01la%2Fuix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roman01la%2Fuix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/roman01la","download_url":"https://codeload.github.com/roman01la/uix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224201924,"owners_count":17272666,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","clojurescript","hiccup","hooks","react"],"created_at":"2024-08-02T13:01:43.710Z","updated_at":"2024-11-12T01:31:28.415Z","avatar_url":"https://github.com/roman01la.png","language":"HTML","readme":"\u003e UIx v1 is not actively maintained anymore, consider using its successor v2 at [pitch-io/uix](https://github.com/pitch-io/uix)\n\n\u003cimg src=\"logo.png\" width=\"125\" /\u003e\n\n_Idiomatic ClojureScript interface to modern React.js_\n\nDiscuss at [#uix on Clojurians Slack](http://clojurians.net/). Bug reports, feature requests and PRs are welcome.\n\nUIx v2 is available at https://github.com/pitch-io/uix\n\n[Try it in online REPL](https://roman01la.github.io/uix/)\n\n[Docs and Guides](https://roman01la.gitbook.io/uix/)\n\n[API Documentation](https://roman01la.github.io/uix/docs/)\n\n_If you like what I do, consider supporting my work via donation_\n\n[![](https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-1.svg)](https://www.buymeacoffee.com/romanliutikov)\n\n[![CircleCI](https://circleci.com/gh/roman01la/uix.svg?style=svg)](https://circleci.com/gh/roman01la/uix)\n\nClojars updates are pushed occasionally, depend via Git deps to get the most recent updates.\n\n[![Clojars Project](https://img.shields.io/clojars/v/uix/core.svg)](https://clojars.org/uix/core)\n[![Clojars Project](https://img.shields.io/clojars/v/uix/dom.svg)](https://clojars.org/uix/dom)\n[![Clojars Project](https://img.shields.io/clojars/v/uix/rn.svg)](https://clojars.org/uix/rn)\n\n```clj\n{:deps {uix.core {:git/url \"https://github.com/roman01la/uix.git\"\n                  :deps/root \"core\"\n                  :sha \"{{replace with commit hash}}\"}\n        uix.dom {:git/url \"https://github.com/roman01la/uix.git\"\n                 :deps/root \"dom\"\n                 :sha \"{{replace with commit hash}}\"}\n        uix.rn {:git/url \"https://github.com/roman01la/uix.git\"\n                :deps/root \"rn\"\n                :sha \"{{replace with commit hash}}\"}}}\n```\n\n```clj\n(require '[uix.core.alpha :as uix])\n(require '[uix.dom.alpha :as uix.dom])\n\n(defn button [{:keys [on-click]} text]\n  [:button.btn {:on-click on-click}\n    text])\n\n(defn app []\n  (let [state* (uix/state 0)]\n    [:\u003c\u003e\n      [button {:on-click #(swap! state* dec)} \"-\"]\n      [:span @state*]\n      [button {:on-click #(swap! state* inc)} \"+\"]]))\n\n(uix.dom/render [app] js/root)\n```\n\n## Recipes\n\n- [State hook](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/state_hook.cljc)\n- [Global state and effects](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/global_state.cljc)\n- [Dynamic styles](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/dynamic_styles.cljc)\n- [Lazy loading](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/lazy_loading.cljc)\n- [Server-side rendering](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/server_rendering.clj)\n- [Interop between UIx and JS components](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/interop.cljc)\n- [Popups](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/popup.cljc)\n- [Error Boundaries](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/error_boundary.cljc)\n- [React Context](https://github.com/roman01la/uix/blob/master/core/dev/uix/recipes/context.cljc)\n\n- Build front-end `clojure -A:dev -m figwheel.main -O advanced -bo dev:prod`\n- Run server `clojure -A:dev -m uix.server`\n- Run front-end recipes in dev `clojure -A:dev:rec-front`\n- Run SSR streaming recipe `clojure -A:dev:rec-ssr`\n\n## Features\n\n### Hiccup syntax extension\n\n- `[:div#id.class]` or `[:#id.class]`\n- `[:\u003e js/Component attrs \u0026 children]` - interop with JS components\n- `[:\u003c\u003e attrs \u0026 children]` - `React.Fragment`\n- `[:# {:fallback element} \u0026 children]` - `React.Suspense`\n\n### Hooks\n\nReact Hooks in idiomatic Clojure style\n\n```clj\n;; state hook\n;; (mutable ref type, re-renders component when mutated)\n(let [state (uix/state 0)]\n  (swap! state inc)\n  @state) ; 1\n\n;; ref hook\n;; (mutable ref type, doesn't cause re-renders)\n(let [ref (uix/ref 0)]\n  (swap! ref inc)\n  @ref) ; 1\n\n;; effect hook\n(uix/effect!\n  (fn []\n    (prn \"after update\")\n    #(prn \"before unmount\"))\n  [deps])\n\n;; convenience macro for uix.core/effect!\n(uix/with-effect [deps]\n  (prn \"after update\")\n  #(prn \"before unmount\"))\n\n;; more in uix.core.alpha ns\n```\n\n### Attributes syntax extension\n\nInjects 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`).\n\n```clj\n(uix.core.alpha/add-transform-fn\n  (fn [attrs]\n    (my-transform-attrs attrs)))\n```\n\n### Hiccup pre-compilation (advanced)\n\n_NOTE: UIx interpreter is already super fast (3x faster than Reagent and only 2x slower than vanilla React).\nUse pre-compilation ONLY if you are hitting performance problems._\n\nCompiles 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`.\n\n```clj\n(uix/html\n  [:h1 \"Title\"])\n\n;; emits this\n{\n  $$typeof: Symbol.for(\"react.element\"),\n  key: null,\n  ref: null,\n  props: { children: \"Title\" },\n  _owner: null\n}\n```\n\n### Lazy loading components\n\nLoading 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).\n\n```clj\n(uix.core.lazy-loader/require-lazy\n  '[uix.components :refer [ui-list]])\n\n[:# {:fallback \"Loading...\"}\n  (when show?\n    [ui-list])]\n```\n\n### Server-side rendering\n\nUIx can be used for SSR or usual templating in both JVM and JavaScript runtimes\n\n#### Server-side rendering in JVM\n\nSee an example in `uix.recipes.server-rendering`\n\n```clj\n(uix.dom/render-to-string element) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostring\n(uix.dom/render-to-static-markup element) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup\n\n;; Streaming HTML\n(uix.dom/render-to-stream element {:on-chunk f}) ;; see https://reactjs.org/docs/react-dom-server.html#rendertonodestream\n(uix.dom/render-to-static-stream element {:on-chunk f}) ;; see https://reactjs.org/docs/react-dom-server.html#rendertostaticnodestream\n```\n\n#### Server-side rendering in JS\n\nSSR works in JavaScript environment via React's serializer using same API.\n\n1. Add `ReactDOMServer` into your dependencies (as `cljsjs/react-dom-server` or any other way)\n2. Run `(uix.dom/render-to-string element)`\n\n## Benchmarks\n\n- Hiccup interpretation `clojure -A:dev:benchmark:bench-front`\n- SSR on JVM `clojure -A:dev:benchmark:bench-ssr`\n\n### Hiccup interpretation\n\n```\nreact x 23866 ops/s, elapsed 419ms\nuix-interpret x 11848 ops/s, elapsed 844ms\nreagent-interpret x 4031 ops/s, elapsed 2481ms\n```\n\n### SSR on JVM\n\n| lib           | test 1   | test 2 | test 3  |\n| ------------- | -------- | ------ | ------- |\n| rum           | 107.8 µs | 3.6 ms | 7.7 ms  |\n| uix           | 120.8 µs | 3.8 ms | 8.1 ms  |\n| uix streaming | 115.7 µs | 3.4 ms | 7.6 ms  |\n| hiccup        | 205.7 µs | 6.5 ms | 16.6 ms |\n\n### TodoMVC bundle size\n\n| lib     | size  | gzip |\n| ------- | ----- | ---- |\n| rum     | 254KB | 70KB |\n| reagent | 269KB | 74KB |\n| uix     | 234KB | 65KB |\n\n## Figwheel\n\nWhen 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.\n\n```clj\n(ns ^:figwheel-hooks my.ns)\n\n(defn ^:after-load render []\n  (uix.dom/render [app] js/root))\n```\n\n## React DevTools\n\nWhen inspecting UI tree in React DevTools, filter out `memo` components to get cleaner view of components tree.\n\n\u003cimg src=\"devtools.png\" width=\"460\"\u003e\n\n## Testing\n\n```\nscripts/test\n```\n\n_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`\nonce in the directory. Otherwise the Node.js version you use is in the `.nvmrc` file. See nvm repo for more documentation._\n\n## Who’s using UIx\n\n- [Zeal (REPL meets Clipboard Manager)](https://github.com/den1k/zeal)\n- [Floor Planner](http://floor-planner.surge.sh/)\n- [Happy Paw mobile web app](https://github.com/roman01la/happy-paw)\n- [ProtonNative app](https://github.com/roman01la/proton-native-cljs)\n- [Lumber](https://lumber.dev) - [source](https://github.com/lumberdev/lumber-site)\n- [GRID](https://theshopgrid.com)\n- [ogre.tools - virtual tabletop](https://ogre.tools/)\n","funding_links":["https://github.com/sponsors/roman01la","https://patreon.com/roman01la","https://www.buymeacoffee.com/romanliutikov"],"categories":["HTML"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froman01la%2Fuix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froman01la%2Fuix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froman01la%2Fuix/lists"}