"Theming" a reagent app

clojurescript

#1

My team and I are starting to build a large website using ClojureScript + Reagent. We’re using both client-side bundles, as well as server-side rendering.

The project itself actually encompasses 3 different brands, which are determined by the URL they access the page from (and member info once they login). The 3 brands are mostly separated by colors & logos, but overall look & feel is the same.

We’ve started down the path of building components out styles & components that are “brand agnostic” and I’m wondering the best way to go about this that won’t be an unmanageable mess.

Potential solutions I’m pondering.

Right now, we’re addressing it by passing the brand in to each component :no_good_man:t2:

Another way I’ve seen this done in the React world was using the Context API to pass things down.

My current too-clever scheme is to use with-redefs before rendering the app to redefine the style(s).

Any thoughts from the community? I’m leaning towards the 3rd one only because it’s so easy, but I have a small feeling that the amount of non-local reasoning it would introduce is going to come back and bite me much later.


#2

You could also use binding instead of redefs, but if you infer your brand from location - why not put it in ratom?


#3

Well, like I said, we’re using server side rendering - which means that any given request that comes in could be from any given brand. So basically we need to not leak the brand between async contexts (“thread-local” doesn’t make sense in JS-land but it’s the same concept). So a ratom wouldn’t work because two requests might accidentally clobber each other as they’re doing all of their data fetching etc.

Which also is why I’ve realized the with-redefs version fails as well :sob:


#4

How do you do server rendering? Is it really async? If not, are you sure your requests for data will interfere with each other somehow? If it’s because you use one atom for your app, we solve than with dynamic var for this atom and ‘binding’.

Edit: read about ratom. Right, well, but that’s like that for every ratom. You will have to solve that problem in general, it’s not about brands. :slight_smile:


#5

We’re using ClojureScript on Node.js & Reagent’s reagent.server/render-to-static-markup to do the server rendering, so yes, it’s async. I suppose this wouldn’t be as much of an issue if we just spawned a Node.js process to do the lift for us :stuck_out_tongue:.


#6

In that case with-redefs won’t work anyway, it’ll work up to first async call. :slight_smile: So yeah, not sure what’s the best way… Maybe React’s context? We use Rum with server rendering right on JVM, so it’s somewhat different.


#7

This might be a bit too big of a change for you, but I’d highly recommend looking at keechma framework for structuring your reagent app.

It allows you to build components that are independent of each other, and you can very easily inject a subscription that depends on the url into each component.

Let me know if you are interested and have any questions about Keechma - I’ve been using it pretty extensively.


#8

Update on this:

I decided to explore using React’s context API for this. The reason why, was:

  1. I’m doing some really clever things to fetch data that my child components depend on on the server side, that force my rendering to happen across many async contexts. Using binding would require me to thread the re-binding through all of those using closures, which seemed like a lot of work.

  2. React context already has a lot of support in libraries (e.g. https://emotion.sh, which I’m using for styling) and so I wanted to come up with a solution that would interop with it.

So I decided to start mucking about with React’s context API, which birthed this library: https://github.com/Lokeh/reagent-context :slight_smile:


#9

If your application is a single page application, or if the branding can be determined on every request (eg. looking at session information), I encourage you to look at a well stablished CSS framework such as Bootstrap and consider applying themes and a custom CSS file per client/brand on top of that.

The custom brand theme would override things like the background image used to display the company logo in the Navbar, some corporate colors and maybe other preferences of your clients (fonts, etc.)