This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-08
Channels
- # babashka (1)
- # babashka-sci-dev (27)
- # beginners (13)
- # cljdoc (1)
- # clojure (24)
- # clojure-austin (1)
- # clojurescript (76)
- # data-science (18)
- # datomic (7)
- # java (2)
- # malli (7)
- # nbb (7)
- # off-topic (34)
- # portal (1)
- # reagent (9)
- # reitit (4)
- # releases (2)
- # remote-jobs (1)
- # shadow-cljs (11)
- # squint (7)
- # tools-build (7)
- # uncomplicate (1)
A few years ago my choice was https://github.com/day8/re-frame/ Anything new from that time to consider as alternative? I am not up to date.
Yes, there are certainly alternatives out there (including uix, helix, "vanilla reagent", fulcro, clojuredart etc). One thing to note now is HTMX (+ _hyperscript where needed) is a game changer and many websites can achieve decent interactivity and performance whilst being fully server side rendered. This has a lot of benefits (one codebase, one programming language, no API needed etc). I can certainly recommend the two stacks that we are currently using at work: Link to overview here (https://clojurians.slack.com/archives/C03RZGPG3/p1671130080367739?thread_ts=1671055359.229919&cid=C03RZGPG3):
Can you explain rationale to choose uix vs re-frame ? I don’t know uix library and description of this library doesn’t describe things in that way.
re-frame
is a framework - it has good documentation and many people like the structure it brings to there UI apps. However, I've noticed that code that uses it [1] often has low re-usability (e.g. there are hard coded keywords everywhere) and [2] often it leads to quite a lot of boilerplate to achieve simple things (e.g. listen for click event, dispatch re-frame event, put event in DB, take event out of DB, dispatch re-frame event, dispatch web request, put web response back in DB, listen for change in DB and finally show result to user).
uix
- is a thin Clojure wrapper around modern react and react hooks and uses hiccup syntax. It's a library and it's up to the user to build there own structure around it. We use integrant to manage/inject our UIX compontents - but you could just use uix on its own.
uix is 100% frontend clojurescript code - I often recommend it when you need highly dynamic frontends (e.g. google Maps, Google Docs) and you want full power of SPA.
For most other usecases, I tend to recommend server side hiccup with HTMX/_Hyperscript for interactivity. Note that you can use ClojureScript to create completely custom HTML tags <my-tag my-arg="hello">
with CustomElement
that you can also use from your server side rendered UI when needed.
HyperScript does not require compilation or extra tools - it can be completely inline within HTML/Hiccup code. It's useful for for very small things (e.g. change an item colour on click). It's built by the same person that built HTMX so they work nicely together. For anything large, it probably better to use ClojureScript or JavaScript (which you can call from HyperScript if needed). When you introduce Clojurescript you add in extra tools (shadow-cljs, NPM) and extra compile time steps etc. So it's only worth adding if there is enough need - not just for something very trivial.
Why not https://biffweb.com/ while you like HTMX ?
I'll check out biff, but it looks like a framework/code generator - HTMX is just a small library that can be used directly.
> I think I got it, although still don’t understand UIx part.
Old react was object based and verbose to use. so reagent
made it much easier to use, then re-frame
wrapped around reagent
to provide a framework for it. Both were and still are quite popular.
However, react
modernised and created hooks, so a thin wrapper around react (like uix
) is all that is needed now. There is no framework (like re-frame
) for uix, so projects have to implement their own structure or use something like integrant
(in frontend ClojureScript). Using uix allows you to use the existing react
ecosystem easily from ClojureScript. There are other clojure libraries like helix that also appear similar to uix.
> have to implement their own structure or use something like integrant Can you extend this part?
Yup those things and also: code structure, State management (between components), Routing of URLs, back/forwards button etc.
No probs. There's a lot of choices for UI in Clojure and re-frame + reagent are still quite popular.
Server side rendered gives you back button for free. Many SPA go with YAGNI. But it can be implemented when needed.
I remember last time I ended in re-frame with my own solutions to manage back/forward buttons etc. It will be nice to use something where I don’t have to
to be precise it was more about route managing, redirect to login when needed etc. some kind of things which you usually do on backend
Do you have an example of public repo of app in UIx + HTMX + hyperscript / UIx + HTMX + cljs ?
Afraid not.
It sounds like Fulcro has some nice features. I haven't tried it seriously enough though to comment.
I haven't used a BE + FE complete solution/framework in a while. Usually I just use a bunch of small Clojure libraries and wire them together with dependency injection (e.g. integrant).
@UJVEQPAKS can you give me examples of use cases when HTMX is a worse choice comparing to pure UIx / re-frame? Just trying to figure out when use HTMX and when not without investing 2 weeks into it.
> Let the server generate the HTML that represents the new application state for that request. Oh do I correctly understand HTMX is about raw HTML server responses instead of JSON response and processing data in web browser to generate HTML?
I mean I think I am not a fun of raw HTML responses from server. I can always change my mind, but at least for now.
Unless in cljs world I can nicely combine HTMX and CLJS to still keep JSON server responses and generate HTML on client side? Does it make sense with HTMX?
ClojureScript (reframe/reagent/uix etc) when you want a full powered SPA. (e.g. you are building the next Google Docs are Google Maps). Or if you just prefer the model of frontend do rendering and backend doing API - which sounds like you do. For many simple CRUD/Dashboard business web applications you can do serverside rendering with HTMX (to achieve interactivity). HTMX let's you do AJAX requests directly from HTML. I don't think HTMX works with JSON APIs - only with HTML.
my understanding of why HTMX exist: because it is easier for developers to make BE HTML rendering instead of FE HTML rendering ? So for example they can use Python instead of some JS framework. I can even agree with this. A few years ago I tried Vue + TypeScript / Angular JS and other solutions and I didn’t like all of them. I was feeling this is wrong. Although the same things with re-frame were ok for me.
Yes - basically HTML was missing a few tags that HTMX adds in which suddenly makes it much more viable to build interactive UIs with just BE HTML. This is a good thing for fans of Python, Ruby on Rails, Scala, Java, Go etc who want to build websites without JavaScript. It's less of an issue for Clojure devs (because we have ClojureScript) - but still useful in Clojure since there is still some friction from writing a separate backend, frontend & API in-between the two etc.
oh UIx is alpha. Did you try https://github.com/lilactown/helix ?
I feel Alpha/Beta/0.1/1.0 doesn't necessarily mean much (e.g. an alpha of one product can be more stable than 1.0 of another). Many Clojure library went from 0.X to 1.0 without changing a single line of code. Helix looks like a good option, but I haven't spent much time with it. I generally prefer hiccup syntax when possible (which uix has). I would certainly give it a go though.
yeah, just UIX alpha + last commit 7 months ago + alpha mean breaking changes and maybe project can be abandoned. Just some fears 😉
Yeah - it's not as active - but it's just a thin wrapper around React so it doesn't need much activity. The features come from the interoperability with the huge react ecosystem - not from the Clojure wrapper. Helix looks more active. There is also https://github.com/pitch-io/uix - which we haven't migrated to since it's not very backwards compatible with https://github.com/roman01la/uix code.
Here is a pretty Good book written by the author of htmx. https://hypermedia.systems/book/contents/ We love hiccup with htmx and a bit of _hyperscript if needed
Can you share your favourite stack with some rationale why this and not other? It will let me catch up things. re-frame? Fulcro? Something else? com.taoensso/timbre for logging? bidi? etc.
PS @U05224H0W I will be glad if you share your stack. (sorry to ask you directly but I trust your experience)
can't share since most of it isn't open source (yet). newest project is using https://github.com/thheller/shadow-grove/ for frontend + https://github.com/thheller/shadow-css/ for css
needless to say if you want something stable and well documented use re-frame or fulcro 🙂
I have been attempting to use fulcro for a few years but continuously got hung up with its rendering model. It still has high leverage due to other features: normalized graph db, seamless integration with pathom, first class transactions with a 'remote' abstraction (so you can easily swap communication strategies like offline, retry logic, caching etc), devtools chrome extension for debugging, form-state management (with minimal diffing sent to server), and is backend agnostic.
After learning re-frame a few years ago I had the insight that subscriptions solve the query + rendering problem way better than the fulcro mode, but using re-frame itself suffers from its own set of problems: up to you to design and adhere to a normalized/semi-normalized db. No server-side rendering support. Everything is keywords which is terrible for editor integration and DX ergonomics (meaning subscriptions are not functions and cannot be used in a non-reactive content, even though there is a PR on re-frame that solves this from https://github.com/day8/re-frame/pull/754).
On top of that the react JS world has moved on from the days when re-frame was designed and has more innovations that are currently being ignored in the cljs community. The first is statecharts (https://xstate.js.org/docs/), yes there are cljs adaptations, but I think the cljs world suffers from not invented here syndrome - XState is awesome and it can easily be used from cljs. The second are hooks. Things like: https://react-hook-form.com/ https://tanstack.com/query/v4 plus the utility https://github.com/streamich/react-use of hooks, the re-frame author stated their https://day8.github.io/re-frame/breaking-it/ ("I will say that I don't think React is on the right track with hooks, Suspense. React was at its best when it tried to be the V in MVC.") This is what dogma looks like, there is no objective statement as to why hooks are bad, just that they are. This ignores the glaring fact that (subscribe [::sub-x])
has the same semantics as (use-subscription [::sub-x])
- it's a hook! Aside from that if you're writing a web app you're dealing with the DOM and the reusable hooks for dealing with that are immensely useful.
Given all of this it was time for me to look for alternatives and cljs does have them - https://github.com/lilactown/helix allows us to co-opt all of the great js hooks while still using cljs. Fulcro is excellent for state management and transactions. re-frame subscriptions are great for dealing with derived data. This all led me to adapt the re-frame subscriptions for broader use: https://github.com/matterandvoid-space/subscriptions
So the "stack" is effecitvely fulcro+pathom with a helix + subscriptions rendering layer. You can view a sample https://github.com/matterandvoid-space/todomvc-fulcro-subscriptions.
That all said, if you're not using a clojure backend where you're serializing with transit and specifically using a clojure-only DB (XTDB, datalevin, etc) I see less and less compelling reasons to stick with cljs for building a modern web app. Because the clojure community seems to hate node.js I don't think we'll ever be able to build tools like https://nextjs.org and meanwhile the ideas that, while yes, were once novel/unique to clojure, are making their way out to other languages (https://github.com/cozodb/cozo, https://github.com/unadlib/mutative) You have to really think about what problems you're trying to solve and what tools will solve those problems. Instead of looking at the world through the tools that are comfortable to you. You might be able to tell I'm no longer convinced cljs has an "unfrair advantage" over the js world. But for certain domains and problems (for me that's full stack graph applications) it does still have a compelling story, so I'm cautiously optimistic.
Did you try https://github.com/lilactown/helix vs https://github.com/roman01la/uix and have opinion?
> clojure community seems to hate node.js node.js on server side? Yeah node.js is definitely my last choice for any project
I didn't try uix because I don't care about using hiccup at the expense of a constant perf cost https://github.com/lilactown/helix/blob/f604dede15905591dd138d6ff13a8baaacbba65b/docs/faq.md#what-about-hiccup
I'm getting this issue using the cljs-oops library:
------ WARNING #1 - :undeclared-var --------------------------------------------
Resource: oops/core.cljs:52:3
Use of undeclared Var cljs.core/js-fn?
--------------------------------------------------------------------------------
I have tried to import the js-fn? macro using:
(ns my.project
(:require-macros [cljs.core]))
but I still get the warning.Actually I ran shadow-cljs info
and it says under dependencies that I'm using [thheller/shadow-cljs "2.8.92" :classifier "aot"]
which uses cljs 1.10.597. I've installed the latest version of shadow-cljs through npm. @U05224H0W do you have any clues?
do you use deps.edn or project.clj to manage dependencies? do you have a :version
hardcoded in shadow-cljs.edn? did you run npm install
in case you manually edited package.json?
I didn't have a :version
hardcoded, but adding :version "2.20.17"
downloads the correct version. I'm not using deps.edn or project.clj to manage dependencies. I ran npm install and my package.json has the correct version. If I create a new project with npx create-cljs-project my-project
and create a build, then run shadow-cljs watch app
it defaults to using 2.8.92.
@U01EAJSJ03Z run via npx shadow-cljs
. otherwise it might use your global install which appears to be the old version. you can update that via npm install -g shadow-cljs
@U05224H0W I figured out what the issue was. I called sudo npm install -g shadow-cljs
in the past so that was overriding the version for my user that's installed with just npm install -g shadow-cljs
. Thanks for the help.