• Stars
    star
    300
  • Rank 138,870 (Top 3 %)
  • Language
    Clojure
  • License
    MIT License
  • Created over 4 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Server rendered UIs over WebSockets

ripley

test workflow

Ripley is a fast server-side rendered web UI toolkit with live components.

Create rich webapps without the need for a SPA frontend.

Comparison with SPA

Single Page Appplications are complicated things, ripley is a traditional server side rendered model with websocket enhancement for live parts.

Pros:

  • No need for an API just for the frontend
  • No need for client-side state management
  • No need to wait for large JS download before rendering (or setup complicated SSR for client side apps)
  • Leverages browser's native routing
  • No separate backend and frontend build complexity, just use Clojure
  • Use functions and hiccup to build the UI, like Reagent or other ClojureScript React wrappers

Cons:

  • Client browser needs constant connection to server
  • Interaction latency is limited by network conditions
  • Unsuitable for serverless cloud platforms

Usage

Using ripley is from a regular ring app is easy. You call ripley.html/render-response from a ring handler to create a response that sets up a live context.

The render response takes a root component that will render the page. Any rendered live components are registered with the context and updates are sent to the client if their sources change. Use ripley.html/html macro to output HTML with hiccup style markup.

Live components are rendered with the special :ripley.html/live element. The live component takes a source (which implements ripley.live.protocols/Source) and a component function. The component function is called with the value received from the source.

(def counter (atom 0))

(defn counter-app [counter]
  (h/html
    [:div
      "Counter value: " [::h/live {:source (atom-source counter)
                                   :component #(h/html [:span %])}]
      [:button {:on-click #(swap! counter inc)} "increment"]
      [:button {:on-click #(swap! counter dec)} "decrement"]]))

All event handling attributes (like :on-click or :on-change) are registered as callbacks that are sent via the websocket to the server. See ripley.js namespace for helpers in creating callbacks with more options. You can add debouncing and client side condition and success/failure handlers.

See more details and fully working example in the examples folder.

Sources

The main abstraction for working with live components in ripley is the Source. It provides a way for rendering to get the current value (if available) and allows the live context to listen for changes.

The source value can be an atomic value (like string, number or boolean) or a map or collection. The interpretation of the value of the source if entirely up to the component that is being rendered.

Built-in sources

Ripley provides built-in sources that integrate regular Clojure mechanisms into sources. Built-in sources don't require any external extra dependencies.

You can create sources by calling the specific constructor functions in ripley.live.source namespace or the to-source multimethod.

Type Description
atom Regular Clojure atoms. Listens to changes with add-watch. See: use-state
use-state Convenient light weight source for per render local state
core.async channel A core async channel
future Any future value, if realized by render time, used directly. Otherwise patched in after the result is available.
promise A promise, if delivered by render time, used directly. Otherwise patched in after the promise is delivered.
computed Takes one or more input sources and a function. Listens to input sources and calls function with their values to update. See also c= convenience macros.
split Takes a map valued input source and keysets. Distributes changes to sub sources only when their keysets change.

Integration sources

Ripley also contains integration sources that integrate external state into usable sources. Integration sources may need external dependencies (not provided by ripley) see namespace docstring for an integration source in ripley.integration.<type>.

Type Description
redis Integrate Redis pubsub channels as sources (uses carmine library)
manifold Integrate manifold library deferred and stream as source
xtdb Integrate XTDB query as an automatically updating source

Working with components

Component functions

In Ripley, components are functions that take in parameters and output HTML fragment as a side-effect. They do not return a value. This is different from normal hiccup, where functions would return a hiccup vector describing the HTML.

Ripley uses the ripley.html/html macro to convert a hiccup style body into plain Clojure that writes HTML. The macro also adds Ripley's internal tracking attributes so components can be updated on the fly.

Any Clojure code can be called inside the body, but take note that return values are discarded. This is a common mistake, forgetting to use the HTML macro in a function and returning a vector. The caller will simply discard it and nothing is output.

Child components

Components form a tree so a component can have child components with their own sources. The children are registered under the parent and if the parent fully rerenders, the children are recursively cleaned up. A component does not need to care if it is at the top level or a child of some other component.

The main consideration comes from the sources used. If the parent component creates per render sources for the children, the children will lose the state when the parent is rerendered.

Dynamic scope

Ripley supports capturing dynamic scope that was in place when a component or callback was created. This can be used to avoid passing in every piece of context to all components (like user information or db connection pools). The set of vars to capture must be configured when calling ripley.html/render-response.

Changes

2023-09-20

  • Bugfix: proper live collection cleanup on long-lived source (like atom)

2023-09-19

  • Bugfix: support 0-arity callbacks when wrapping failure/success handlers

2023-09-16

  • Alternate server implementation support (with pedestal+jetty implementation)

2023-09-09

  • Support dynamic binding capture

2023-09-02

  • Support a ::h/live-let directive that is more concise

2023-08-30

  • Log errors in component render and callback processing

2023-08-26

  • Fix bug in live collection cleanup not being called

2023-08-04

  • Fix computed source when calculation fn is not pure (eg. uses current time)

2023-07-01

  • Added ripley.js/export-callbacks to conveniently expose server functions as JS functions
  • Added static utility to use a static value as a source

2023-06-28

  • Source value can be nil now and component is replaced with placeholder

2023-06-10

  • Support client side success and failure callbacks

2023-06-07

  • ripley.html/compile-special is now a multimethod and can be extended

2023-03-18

  • Support specifying :should-update? in ::h/live
  • use-state now returns a third value (update-state! callback)

See commit log for older changes

More Repositories

1

specql

Automatic PostgreSQL CRUD queries
Clojure
132
star
2

clj-chrome-devtools

Clojure API for controlling a Chrome DevTools remote
Clojure
130
star
3

pgprolog

PostgreSQL Prolog language handler
Rust
128
star
4

xtdb-inspector

Web UI for inspecting XTDB (v1) database
Clojure
91
star
5

postgrest-ui

ClojureScript UI components for PostgREST
Clojure
57
star
6

Webjure

Webjure, a web programming framework for Clojure
Clojure
32
star
7

bearsql

Bare words SQL macro for Clojure
Clojure
32
star
8

tuck

Tuck: a micro framework for Reagent apps
Clojure
29
star
9

re-svg-icons

Reagent SVG icons
Clojure
28
star
10

clj-prolog

Clojure interface to Prolog
Clojure
21
star
11

bb-lambda

AWS Lambda custom runtime for Babashka scripts
Clojure
19
star
12

xtdberl

Erlang/Elixir interface to XTDB (v1)
Erlang
17
star
13

REPLey

A web REPL made with Ripley
Clojure
16
star
14

re-html-template

Generate hiccup (reagent/server rendering) components from HTML templates at compile time
Clojure
14
star
15

LiveWeb

LiveWeb: web app framework for Smalltalk
Smalltalk
13
star
16

json-schema

JSON schema validator
Clojure
12
star
17

nrepl-doc-inject

NREPL middleware to inject community docstrings
Clojure
12
star
18

drtest

Declarative Reagent testing library
Clojure
12
star
19

eta-jdbc-example

Simple example of using JDBC from Eta language
Haskell
10
star
20

reagent-leaflet

Use LeafletJS maps as Reagent components
Clojure
9
star
21

pows

Playwright over WebSocket
Clojure
7
star
22

mato

Simple reagent SVG worm game for educational purposes
Clojure
6
star
23

koukku

Yet another small React wrapper with hooks
Clojure
5
star
24

rolf

Reagent OpenLayers Functions: a simple Reagent wrapper for OpenLayers
Clojure
5
star
25

pharo-Pows

Pharo library for using Playwright over WebSocket
Smalltalk
5
star
26

swixt

SWI-Prolog library for XTDB v2
Prolog
5
star
27

widgetshop

Simple Reagent example.
Clojure
4
star
28

asar

Read ASAR archive files from Clojure
Clojure
4
star
29

tulivarasto

Simple Google Cloud Firestore library for Clojure
Clojure
3
star
30

matofs

Simple F# SVG worm game (Fable React)
F#
3
star
31

electric-examples

Electric Clojure examples and experiments (based on starter app)
CSS
2
star
32

pharo-XTDB

XTDB client for Pharo Smalltalk
Smalltalk
2
star
33

raylib-smalltalk

Smalltalk bindings to Raylib
Smalltalk
2
star
34

edn-to-cfn

See: portkey-cloud/edn-to-cfn
Clojure
2
star
35

tuck-remoting

Supplemental remoting library for Tuck: send events between client and server
Clojure
2
star
36

elmato

Simple Elm SVG worm game
Elm
2
star
37

pharo-EDN

Extensible Data Notation library for Pharo Smalltalk
Smalltalk
2
star
38

mappy

An experimental pure Reagent web map library
Clojure
2
star
39

pharo-EmacsKeys

Some emacs like key bindings to make Pharo code editing feel more at home
Smalltalk
2
star
40

fileyard

Trivial file storage
Clojure
1
star
41

jiradash

Emacs JIRA dashboard
Emacs Lisp
1
star
42

iwantmylunch

A simple Swift command line utility to check your Edenred lunch card balance
Swift
1
star
43

aoc2021-smalltalk

Some Advent of Code 2021 learning using Smalltalk
Smalltalk
1
star
44

pharo-Kafka

Pharo bindings to librdkafka C client library.
Smalltalk
1
star
45

express-htmx

CSS
1
star
46

aoc2023-prolog

Advent of Code 2023 in Prolog
Prolog
1
star