I guess all the cool kids are using brotli now, that does seem to give another 20% over gzip over here
The coolest kids switched to zstd :D but yeah brotli is really good, 10mb cljs bundle outputs around 2.5mb
JS generated from cljs seems to be very compression friendly, I assume because the same code gets generated for common forms
yeah, it's very regular
are there any ClojureScript native morphing libraries? I feel like this is something that would make ClojureScript a lot more useful out of the box w/o using random deps to get functional UI patterns
To be honest, I'm not even sure what you're asking exactly. What's "morphing" in this context, especially given that we're in #cljs-dev and not #clojurescript?
the last is the most robust wrt. DOM state (scroll position, input focus) etc. - it's not that big
https://dev.37signals.com/a-happier-happy-path-in-turbo-with-morphing/
it's 2025, I think having a standard solution to this problem would be good to avoid taking on a dependency - hiccup also isn't going anywhere - it would be interesting to support both morphing DOM to DOM, but also Hiccup -> DOM - with special taken around DCE so you only pay for the strategy you use.
I think https://github.com/thheller/shadow-grove is relevant, at least it talks about DOM diffing, so maybe Thomas can chime in.
not a generic morph lib, also not hiccup. well the syntax yes, but not actual vectors and stuff in the end ๐
replicant exists if you can stomach the "interpreted hiccup" slowness. not that morph is fast by any means
not aware of anyone having implemented just morph in CLJS. kinda no point it those libs exists to go from DOM->DOM. hiccup is a different story, but if you are diffing that you might as well use replicant
DOM->DOM could still be useful if you're doing an htmx / datastar style app like the Turbo example. I don't think that approach should be ruled out for CLJS. i.e. you have some CLJS that manages a a nice interactive component, but you don't want to deal w/ all this app state on the client.
idiomorph is one file. could probably just wrap that as a goog.module and go ๐
yeah, no deps, very sensible - but I don't think it has alternate tree rep hooks?
I'm thinking more along the lines of transit - so lower level thing just written in JS
so DOM<->DOM is not adding much to the bundle - and then Hiccup bit on top of that
and importantly, not really trying solve the more general problem of building apps - just a layer to support more functional DOM interactions
replicant also has morphing and hiccup I think? I'll let @christian767 explain if he wants
It has hiccup, not entirely sure what morphing is. I have a feeling it's even more low level/barebones than Replicant?
Just looked at your example, and that looks a lot like what Replicant does, so maybe?
morphing is just diffing and re-rendering the dom
I think
In that case, yes, that's what replicant does ๐
I heard @mkvlr say in an interview that he would like to use replicant to make the "front end smaller" and "to do more on the server". Not sure if I understood well what he meant. I see replicant as a more functional way of managing state on the UI, but it's definitely not the same as HTMX where you render the HTML on the server and then send it to the client -- right?
No, it's not like HTMX. It's closer to something like "functional React". However, since it doesn't allow you to put any state management inside the rendering components, you're forced to do centralized state management. If you want to stick that part on the server and just render on the client that will work just fine.
Replicant doesn't really manage state, it just provides infrastructure for you to do it yourself.
That's what I tell people who use my libraries as well. Just do it yourself man
;)
๐
Replicant is a rendering library
It does that for you
yes, that's how I understood your presentation as well. Wondering what @mkvlr meant in the interview though
I meant the ability to render hiccup either to strings on the server or on the client, having the views in cljc files
If I've understood the talk correctly this is more for testing purposes than for keeping thing smaller?
I meant being able to render some views which donโt need interactivity purely on the server to a string, while also supporting building a spa for parts that need it and sharing the view code for both
replicant.string can render static HTML just fine, we do that as well ๐
I have also experimented with some HTMX-inspired stuff to allow static HTML rendered this way have event handlers. But I have too many ideas and projects and too little time ๐
@borkdude I didn't touch on this use of Replicant in the talk. Probably should have, but there's only so much one can do in 30mins.
sure, but why not just use hiccup instead of replicant if you're going to do HTMX(-ish)?
Replicant supports some features not in hiccup, so if you're using Replicant on the client, its string renderer gives you the exact same semantics for the server.
If you're not using Replicant on the client at all, there probably is no point ๐
right
The way we build our web app at work, we have a page abstraction with a render function. Its default mode is to render static HTML on the server. When we need more interactivity, we set :client-page? true and it runs render on the client instead. If the page also needs live data, we set :streaming-data? true and it uses web sockets to stream data. Pretty awesome to have the UI slide seamlessly between the server and client like that.
replicant is also interesting, but yeah I think something that's just Closure aware JS at the bottom but w/ an eye for CLJS integration is missing
why would it be necessary to add this to CLJS, people could also just use idiomorph as a lib - what are the benefits? are you thinking of some hiccup thing that integrates well with it?
because dependencies on NPM are undesirable for something so basic?
diff / morph is at this point no longer a fringe thing
also the base thing wouldn't be in ClojureScript, goog.dom.morph or something like that
NPM / JS common practice is mostly a trash fire that we have to put up with - I don't think you should have to go there if you don't want to.
The exact reason why I wrote Replicant ๐
stuff like that is of course nice, but I think we're approaching the moment where just using GCL and CLJS is preferred
unfortunately requires a login - but is a 17 minute read
every serious CLJS project that requires Node / NPM also takes on this crazy burden of reasoning about the final artifact
Same article without login: https://archive.is/20250424210559/https://javascript.plainenglish.io/how-we-reduced-our-javascript-bundle-size-by-70-while-adding-features-cb784a948631
squint is it's own thing, it's not a thing you add to cljs. not sure if you understood that. I was just showing off basically ๐
no no, I get that I meant - I know you can do that ... idiomorph doesn't suffer from the usual problem!
rather, I think everyone is familiar w/ getting things from NPM - but dealing w/ that stuff is just more annoyances
the ease / peace of mind when something is in GCL is very high (the doc issue aside)
got it
and to be clear, I think CLJS integration w/ Node.js / NPM is all fine and good - it's working as well as it would for JS dev
but I think having something that's just CLJS-ready would encourage some community exploration of simpler results
So 15 years ago you could have added jquery to GCL
no need because GCL could already do what jquery did back then
that was relatively clear after the initial release
yet a lot of people were using jquery in addition to ClojureScript back when it got started?
mostly because people didn't know what was in GCL - old habits die hard - not surprising
also there's nothing wrong w/ a jQuery like approach even today for lot of things you might want to do. that's more or less how I used CLJS for my blog
and I would still use it that way if/when I revive the blog
and I don't want NPM deps on my blog thank you very much ๐
the CLJS printing machinery is significantly cleaner now wrt. DCE. much easier to reason about
advanced compiling a trivial source file w/ just a vector is now 32K / 9K gzipped
a keyword by itself is 6K (not even gzipped)
persistent hash maps pull in significantly more stuff, but even that's smaller 88K / 20K
I don't think it will help most programs, but now the results are closer to matching expectations of what Closure can do. and definitely makes it easier to experiment w/ whether "light weight" data structure replacement could provide optional savings.
having unused persistent data structures at the top level doesn't seem to cause any problems - Closure does eliminate them
I would summarize the long-standing issue as not so much printing itself as much as not using JS primitives in the printing implementation
There are also a lot cycles between definitions, the one between IndexedSeq and str was particularly bad