Isomorphic web frameworks solving the JavaScript Problem.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.

(Possibly awesome) Web frameworks solving the Javascript problem

Because writing a web project with two languages is frustrating, specially with Javascript.

We'll list here isomorphic web frameworks, that allow to write an interactive web app without resorting to Javascript. Bonus point to the ones not using JS libraries and not separating back and front code.

Directly inspired by

Table of Contents

Clojure & ClojureScript

! heavily uses the JS ecosystem (specially React).

! Clojure and ClojureScript are two different things.

Common Lisp


With Weblocks, you can handle all the business logic server-side, because an action can be any lisp function, even an anonymous lambda, closuring all necessary variables.

Similar to React but server based.

Editor's note: allows to keep the same CL development experience all the way. Very lightweight.



Drab is the extension library to Phoenix Framework for providing an access to the browser's User Interface (DOM objects) from the server side. The main advantage is to eliminate necessity of writing two applications: one for the client-side, and one for the backend. All the UI control may be now done in the backend, eliminating JS and AJAX.

Example: simple html on the client side:

  <input name="text_to_uppercase" value="<%= @text %>">
  <button drab="click:uppercase">Upcase</button>
  <button drab="click:downcase">Downcase</button>

Drab does not GET or POST the form, it calls the event handler function via websockets instead, and updates the DOM nodes directly on the page. Clicking the button does not reload the page.

In this example, the click event in the browser remotely runs DrabPoc.LiveCommander.uppercase/2 on the server:

defmodule DrabPoc.LiveCommander do
  use Drab.Commander

  defhandler uppercase(socket, sender) do
    text = sender.params["text_to_uppercase"]
    poke socket, text: String.upcase(text)

After processing the inputs, we need to present it to back in the browser. This is where we use poke/2 function - it pushes the assign (in this case: <%= @text %>) back to the browser. Input value is updated, without reloading the page.


Uses the GopherJS transpiler.





Single page applications for Nim.



Eliom is a multi-tier framework for developing multi-platform Web and mobile apps. It transforms OCaml into a multi-tier language: It makes it possible to write modern distributed applications fully in OCaml, for both the server and client parts, which simplifies a lot the communication between server and client. Applications are written as single programs that can run on any Web browser or mobile device (iOS, Android), saving from the need to develop one version for each platform.

Eliom has support for reactive pages (generated on server or client), advanced session mechanism, server to client communication, continuation based Web programming, etc.

It can also be used for more traditional Web or mobile apps: server only, single page applications, REST API, etc.

Excerpt from the tutorial:

The following code defines a client-server Web application with only one service, registered at URL / (the root of the website).

The code also defines a client-side application (section [%%client ... ] ) that appends a client-side generated widget to the page. Section [%%shared ... ] is compiled on both the server and the client-side programs. Inside such a section, you can write let%server or let%client to override [%%shared ... ] and define a server-only or client-only value (similarly for [%%server ... ] and [%%client ... ] ).

module Ex_app =
  Eliom_registration.App (struct
    let application_name = "ex"
    let global_data_path = None

let _ = Eliom_content.Html.D.(
    ~path:(Eliom_service.Path [""])
    ~meth:(Eliom_service.Get Eliom_parameter.unit)
    (fun () () ->
         (Eliom_tools.D.html ~title:"tutowidgets" ~css:[["css"; "ex.css"]]
            (body [h2 [pcdata "Welcome to Ocsigen!"]])))

let mywidget s1 s2 = Eliom_content.Html.D.(
  let button  = div ~a:[a_class ["button"]] [pcdata s1] in
  let content = div ~a:[a_class ["content"]] [pcdata s2] in
  div ~a:[a_class ["mywidget"]] [button; content]

let _ =
  let%lwt _ = Lwt_js_events.onload () in
    (Eliom_content.Html.To_dom.of_element (mywidget "Click me" "Hello!"));
  Lwt.return ()

The ## is used to call a JS method from OCaml and ##. to access a JS object field.

Lwt is the concurrent library used to program threads on both client and server sides. The syntax let%lwt a = e1 in e2 allows waiting (without blocking the rest of the program) for an Lwt thread to terminate before continuing. e2 must ben a Lwt thread itself. Lwt.return enables creating an already-terminated Lwt thread. Lwt_js_events defines a convenient way to program interface events (mouse, keyboard, ...).

Lwt_js_events.onload is a Lwt thread that waits until the page is loaded. There are similar functions to wait for other events, e.g., for a click on an element of the page, or for a key press.

Editor's note: quite some work to install, compile and deploy.



[me] Reminds me of Nagare.

[Anpylar team] AnPyLar has gone a step beyond by being client-side. One of the advantages is that the application is downloaded once, and can contain multiple pages and hierarchies, which are navigated without sending a single request to the server.

The server can with it be a pure API server (and the server of the initial content download), with the advantage of no longer having to implement business logic in the server.


Editor's note: looks like ASP .Net. Doesn't seem totally JavaScript free:

@presentation.render_for(Board, 'switch')
def render_Board_item(self, h, comp, *args):
    reload_search = ajax.Update(component_to_update='show_results',
                                render=lambda renderer: comp.render(renderer, 'search_results'))
    h << h.script(u'''$(window).on('reload_search', function() { %s; })''' % reload_search.generate_action(41, h))

    with h.div(id='switch_zone'):
        if self.model == 'columns':
            search_cb = ajax.Update(
                render=lambda renderer: comp.render(renderer, 'search_results')
            ).generate_action(1, h).replace('this', 'elt')
            oninput = 'debounce(this, function(elt) { %s; }, 500)' % search_cb
            # etc



Inesita is a simple, light, Ruby front-end framework. Yes, Ruby, it’s all about Ruby, and its ecosystem.

Volt [staling]

A Ruby web framework where your Ruby runs on both server and client




Thanks for doing so ! We didn't try everything let alone have we a good experience with all of them, so we'll appreciate your light.

On this readme, we'd have quick links and highlights. We can have discussions in issues and more in-depth resources and explanations in a new file.