Web development with ClojureScript

I like garden but I don’t have the impression it’s used that much in practice. My impression is companies use whatever is popular in the frontend world for their CSS, combined with node based tooling.

Learning reagent/re-frame is a solid investment. These or some variant thereof seem to be the most popular options for cljs based UI stuff.

Also keep in mind there’s often a distinction between people doing frontend development and people working on design and CSS. You should know enough CSS to get around, but to know the ins and outs is really a specialization in itself.

1 Like

I had brief touch with reagent and re-frame together with shadow-cljs some time ago. Is shadow-cljs still in use? What’s you choice when it comes to build system? Is there anything else when it comes to nodejs and npm interop? Maybe lumo?

Lumo is afaik no longer maintained, and is of pretty niche use in any case. I occasionally use it if I really quickly want a cljs repl to try something out. I used to think it was a good option for scripting but these days would recommend babashka.

Shadow-cljs is very solid, feature complete, well maintained, and in particular makes it easy to use js dependencies straight off npm. cljs-main and figwheel-main together are perhaps still more commonly used and are also a very solid, somewhat more conservative choice.

When it comes to npm interop there are really two options that work reliably: only use libraries from cljsjs, or use shadow. Anything else requires some pretty deep understanding of clojurescript and the google closure compiler.

2 Likes

I was in the same position as you, and I ended up very satisfied with rum and reforms. The advantage of rum over reagent is that rum has server-side rendering in Clojure. I can write most of my HTML components in a cljc file, render them efficiently to HTML on the server, and the client hooks into the components effortlessly. It was fairly easy to go from having no CLJS experience to having a slick editor with a live preview. Image uploads and text editing were harder, but doable with the clsjs packages and learning more about React components and Rum mix-ins. Fun fact: At one point I was testing reagent and after switching back to rum, ended up still using the reagent reform bindings. They worked just fine with rum, I assume because they just create React components.

I tried fulcro, but it seems maybe too fancy. It might be nice if you are already using a graph database, but it’s pretty awkward to bolt on to a document database. After porting all my interface code, it seemed like I was going from straightforward composeable systems to one big inter-connected monolith. In particular, although reforms is highly composeable and has bindings for three different react libraries, it does not for fulcro. I would still like to try it if I end up with a use-case where I’m using Datomic, and once the fulcro-rad stuff has matured and stabilized a bit.

2 Likes

What’s the latest preferred way to communicate between front end cljs and back end clj?

Rum, Sente, and Semantic-UI for CSS have served me very well so far.

I just had a look at Semnatic UI, and to me, it looks simpler than other CSS frameworks – you don’t seem to have to know loads of wrapper classes to produce something useful.

Would that be an accurate description, in your opinion? Would you like to expand on your reasoning behind choosing Semantic UI?

That is a harder question, this seems to be an area where folks are still figuring stuff out. It also depends on what your needs are on the backend in terms of storage and processing. Are you doing some form of event sourcing? Is your datastore immutable? Do you need to broadcast updates back to clients? It all just really depends what you’re building.

Judging by the projects I’ve been on the last few years I’d say sending re-frame style events over a websocket has become pretty common, combined with either a classical API or graphql/pathom for bootstrapping client state and for side-loading data.

1 Like

How do you make hooks on the client side? Could you show me any example? Do you make the same components by invoking rum/hydrate? It’s interesting problem for me, because I don’t know how can I pass handlers (from client side) properly into rendered components by server. Thanks!

1 Like

How do you make hooks on the client side? Could you show me any example? Do you make the same components by invoking rum/hydrate? It’s interesting problem for me, because I don’t know how can I pass handlers (from client side) properly into rendered components by server. Thanks!

I have a ui.cljc file used by both client and server:

(ns dual.ui
  (:require [rum.core :as rum :refer [defc]]))

(defn react [id]
  #?(:clj @id
     :cljs (rum/react id)))

(defc post-content [post]
  [:div.post
    ...your code here...])

react here is mostly used when the data has to be retrieved by an AJAX request, so I can just pass an atom that gets updated whenever the AJAX request finishes and it just works. On server-side, you pass in an atom or future. In this example, I am not passing atoms or using the react function since it’s not needed normally, but when you need it, it is there.

I use this file for HTML and some functions that are used by both client and server, but nothing specific to the client or to the server.

The server-side rendering is in views.clj:

(ns dual
  (:require [dual.ui :as ui]
            [rum.core :as rum]))

(defn post-page [post]
  (rum/render-hml
    [:html
      [:body
        [:div#post-id {:style "display: none"} (:id post)]
        [:div#post-content-container (post-content post)]]])

I have my client code in admin_ui.cljs

(ns dual.admin-ui
  (:require [domina :as d]
            [dual.ui :as ui]
            [reforms.rum :as f]
            [rum.core :as rum :refer (defc defcs react)]))

(defc post-content
  < rum/reactive
  [post-atom]
  (let [post (react post-atom)]
    (ui/post-content post)))

When the ClojureScript runs, it grabs post-id from the div, does an AJAX request to get the post content as a map, setting post-atom’s value, and runs this callback:

(fn [_]
  (rum/hydrate (post-content post-atom) (d/by-id "post-content-container"))
  (render-post-editor post-atom))

render-post-editor is just a form created by reforms that syncs to the post-atom, so whenever the form is changed, the displayed page updates to match it. The editor is side-by-side with the displayed page. For values that require DB lookups (say, if you wanted to show the “next post” title but the field provided is just the next post’s ID), you create an atom and write your ui.cljc code to use the react function. You do an AJAX request that updates the atom, and it renders correctly once complete (the relevant section will just be blank until then).

There is probably a cleaner way to do this, this is just something I got working on my first go and has been easy to extend/change when needed. reforms is very easy to set up and works brilliantly, and this is a fairly easy way to take advantage of it.

I don’t see it mentioned on this thread yet, so I’ll put a massive shout-out to @Yogthos and Luminus, which got me started when I discovered the book of EDIT: ALMOST the same title as this thread, and I’ve been happily web-deving with Clojure stacks ever since. Using Luminus projects as templates and the book/docs as a reference, all my questions about how to do those common things (e.g. front-end/back-end) all went away. Eventually I grew to where Luminus was really a recommendation of packages and code-layout, and then I started tweaking it to fit my needs.

1 Like

I just realized the important difference: this post is specifically about Clojurescript. I still recommend the book above, which got me started with cljs as well, but will read more carefully for ideas about how a cljs backend is different than a clj/java one.

Umm… Suppose you have component in cljc similar to post-content but with extra button, how do you bind a event like on-click on this button with a function handler?

I’d like to have one version of UI. One with handlers for client and the same without handlers for server (SEO purpose).

It’s possible with rum?

I picked it because it had complete styling for a large set of components, including good support for forms. I’ve been pretty happy with it, but I’m trying to eliminate all its JavaScript as I go (it doesn’t play well with a React-based ClojureScript app and I really would rather not pull in a jQuery dependency). I got rid of all the JavaScript except for popups.

It is not an ideal solution, because the main Semantic-UI code base seems to be abandoned right now. There is a fork (Fomantic-UI) which seems to have goals that are not aligned with mine: I am all about stability and long-term support, while Fomantic-UI community favors quick development and massive changes. Still, I am fairly happy with my choice from a few years back.

1 Like

Just to make sure that we are on the same page - my question was about developing frontend, not backend in ClojureScript.

1 Like

Umm… Suppose you have component in cljc similar to post-content but with extra button, how do you bind a event like on-click on this button with a function handler?

I’d like to have one version of UI. One with handlers for client and the same without handlers for server (SEO purpose).

It’s possible with rum?

It’s possible, it just wasn’t my particular use case. Using the reforms example

  [:button {:type "button"
            :class "btn btn-primary"
            :onClick #(js/alert (:name @data))} "Submit"]]]

You could put this in ui.cljc using reader conditionals:

  [:button {:type "button"
            :class "btn btn-primary"
            #?@(:cljs :onClick #(js/alert (:name @data)))} "Submit"]]]

The problem here is that you probably want the onClick callback to reference your other ClojureScript code, but you don’t want that code in ui.cljc. I would try passing in a map of additional attributes that can include a handler like so:

(defc button-component [& [attrs]]
  [:button (merge
                {:type "button"
                :class "btn btn-primary"}
                attrs)
            "Submit"]]]

Then your cljs file would pass along a map like {:onClick #(js/alert (:name @data))}.

If you are doing this with a lot of handlers then it might be unwieldy (since most of the handlers would be passed along a chain of several functions), but the best solution is highly dependent on the specifics of your project.

This is not meant as a final architecture necessarily, but it should get you going and in a position to decide how you want your code laid out for your use case.

1 Like

In that case, my original recommendation to go with Luminus stands. It is well-documented and will get you a working thing (with cljs) in moments, and then you can take the working example from there to see what you want.

i find we’re being quite productive at multis.co with re-frame and something like Tachyons for CSS - we don’t have to mess with stylesheets :slight_smile:

Shadow-cljs is perfect for interop with other js libraries and actually we also use it to compile to node for our Cloud Functions

but imho ClojureScript really shines on the frontend

@otwieracz

I understand your question, but i would urge you to start by understanding the browser before you start tinkering with front end tools. I find that only by understanding the underlying plateform and networks can you start to navigate choices and arrive at solutions that fit a clients needs.

E.g Understand how a webpage created start to finish from the server to displaying to the client. Then be able to discuss what (for example) react adds to that process.

Without that background trying to understand the tradeoffs various solutions are offering will be very hard.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.