/awesome-no-js-web-frameworks

Isomorphic web frameworks solving the JavaScript Problem.

(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 https://wiki.haskell.org/The_JavaScript_Problem.

Table of Contents

Clojure & ClojureScript

! heavily uses the JS ecosystem (specially React).

! Clojure and ClojureScript are two different things.

Common Lisp

CLOG

CLOG - The Common Lisp Omnificent GUI. Uses web technology to produce graphical user interfaces for applications locally or remotely.

  • CLOG is based on the ideas of GNOGA, a framework the author wrote for Ada and used in commercial production code since 2013.
  • many tutorials

ISSR

Interactive SSR - ISSR allows you to make interactive web pages without writing client scripting. No knowledge about Javascript or DOM is necessary.

Weblocks

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.

Components-based like React, but server based.

Editor's note: it allows to keep the same CL development experience all the way. Very lightweight. We can keep the state of the application between changes.

Elixir

Drab

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:

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

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)
  end
end

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.

Phoenix LiveView

Phoenix LiveView is an exciting new library which enables rich, real-time user experiences with server-rendered HTML. LiveView powered applications are stateful on the server with bidrectional communication via WebSockets, offering a vastly simplified programming model compared to JavaScript alternatives.

Go

Uses the GopherJS transpiler.

Haskell

https://wiki.haskell.org/The_JavaScript_Problem

Haste

Haxe

Haxe is an open source high-level strictly-typed programming language with a fast optimizing cross-compiler. Haxe can build cross-platform applications targeting JavaScript, C++, C#, Java, JVM, Python, Lua, PHP, Flash and allows access to each platform native capabilities. Haxe has its own VMs (HashLink and NekoVM) but can also run in interpreted mode.

Web frameworks:

Example: ufront hello world.

class HelloWorld {
	static function main() {
		#if server
			// Initialise the app on the server and execute the request.
			var ufApp = new UfrontApplication({
				indexController: HelloWorldController,
				defaultLayout: "layout.html"
			});
			#if (php || neko)
				ufApp.executeRequest();
			#elseif nodejs
				ufApp.listen();
			#end
		#elseif client
			// Initialise the app on the client and respond to "pushstate" requests as a single-page-app.
			var clientApp = new ClientJsApplication({
				indexController: HelloWorldController,
				defaultLayout: "layout.html"
			});
			clientApp.listen();
		#end
	}
}

Example: HexMachina Flickr gallery.

Nim

Karax

Single page applications for Nim.

Ocaml

Eliom

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
  end)

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

[%%client
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
  Dom.appendChild
    (Dom_html.document##.body)
    (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.

PHP

Livewire

A full-stack framework for Laravel that takes the pain out of building dynamic UIs.

Python

Anpylar

[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.

Nagare

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(
                action=self.search,
                component_to_update='show_results',
                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

Reactor

Phoenix LiveView but for Django

In x-counter.html:

{% load reactor %}
<div {% tag_header %}>
  {{ amount }}
  <button @click="inc">+</button>
  <button @click="dec">-</button>
  <button @click="set_to {amount: 0}">reset</button>
</div>

When the increment button receives a click event send(this, 'inc') is called, send is a reactor function that will look for the parent custom component and will dispatch to it the inc message, or the set_to message and its parameters {amount: 0}.

in live.py:

from reactor import Component


class XCounter(Component):
    template_name = 'x-counter.html'

    def __init__(self, amount: int = 0, **kwargs):
        super().__init__(**kwargs)
        self.amount = amount

    def inc(self):
        self.amount += 1

    def dec(self):
        self.amount -= 1

    def set_to(self, amount: int):
        self.amount = amount

IDOM, Justpy…

  • IDOM. Server-side bindings for React.

Ecosystem independence is also a core feature of IDOM. It can be added to existing applications built on a variety of sync and async web servers, as well as integrated with other frameworks like Django, Jupyter, and Plotly Dash.

JustPy is an object-oriented, component based, high-level Python Web Framework that requires no front-end programming. With a few lines of only Python code, you can create interactive websites without any JavaScript programming.

JustPy has no front-end/back-end distinction.

JustPy's frontend (which is transparent to JustPy developers) is built using Vue.js

Ruby

Hyperstack

! heavily uses the JS ecosystem.

status of December, 2020: v1.0 is on its way.

Inesita

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

Rust

Various isomorphic Rust frameworks are in development.

Seed

Seed compiles to WASM and follows the Elm architecture.

Smalltalk

Seaside

HTML on the wire

Libraries that are usable with any backend solution.

HTMX

HTMX

allows you to access AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes, so you can build modern user interfaces with the simplicity and power of hypertext.

I add it on this list because we can achieve quite a lot without writing a line of JavaScript.

From the doc:

<button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML"
>
    Click Me!
</button>

This tells htmx:

"When a user clicks on this button, issue an HTTP POST request to '/clicked' and use the content from the response to replace the element with the id parent-div in the DOM"

So, your backend returns HTML fragments. No JSON API involved.

What is nice, is that we can update more than one part of the page from the same backend response. Just return several HTML chunks in your response and tell HTMX what part of the front they should update (see hx-swap-oob, or how to tell HTMX to send an event after the first response is processed).

Unpoly

See also: https://unpoly.com/

Unpoly enables fast and flexible frontends while keeping rendering logic on the server. It has no dependencies and plays nice with existing code.

Contributing

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.

See also

a list of libraries for creating web applications that handle user interaction with the DOM on the server. These libraries take a different approach from older server-driven browser UIs that simulated a desktop GUI toolkit. They do not lock the developer into working with predefined components; they operate at the level of HTML (DOM) rather than GUI widgets.