A toolkit for building web components in Clojure and ClojureScript.
Simple extensions to the browser. Extend the browser as a hypermedia client. Do you wish the form
element would update part of the DOM rather than reload the whole page? Build a custom component that does just that.
Design Systems. Web components are great for design systems. Build a design system in Clojure(Script), and share it with other teams regardless of the stack they use.
Micro-frontends. Web components are ideal for micro-frontends. Your team can use Zero alongside other teams using React or Svelte - in the same application.
State-heavy SPAs. Zero provides a set of state management tools from the simple and easy to the simple and sophisticated. Start easy and scale up as you need to.
Zero gives you tools for reactive state. You can choose to stick close to the DOM, following HATEOS principles. You can use a central in-memory application database similar to re-frame. Or you can choose from intermediate options -- how you manage state is up to you.
Let's show the different options using the same example: an incrementing button. We want a button that reads "Clicked 0 Times". Every time you click it, it should increment the counter.
You can render this button wherever you choose: in React, Svelte, pure HTML. It looks like this:
<increment-button clicks="0"></increment-button>
This can be implemented a few different ways. We will consider several examples, moving from the low level to the high level.
Zero makes custom component reactive. Similar to React's notion that a view is a function of its props, we can render a web component as a function of its properties.
(ns increment-counter
(:require [zero.core :as z]
[zero.config :as zc]
[zero.component]))
(defn on-click
[event]
(let [increment-button (.-host (.-currentTarget event))
clicks (js/parseInt (.-clicks increment-button))]
(set! (.-clicks increment-button) (inc clicks))))
(defn button-view
[{:keys [clicks]}]
[:root> {::z/on {:click on-click}}
[:button (str "Clicked " clicks " times")]])
(zc/reg-components
:incrementing-button {:view button-view
:props #{:clicks}})
When we register our component, we declare clicks
as a prop. When we update the incrementing-button
's clicks
property, it will re-render.
To update our component, we use use the zero.core/on
prop on the root to listen for click events. When the click occurs, on-click
is called, and it increments the incrementing-button
's clicks
property. incrementing-button
re-renders automatically.
We can also use ClojureScript's built-in tools for state. Let's look at an example using an atom.
(ns increment-counter
(:require [zero.core :as z]
[zero.config :as zc]
[zero.component]))
(defonce clicks*
(atom 0))
(defn on-click
[_event]
(swap! clicks* inc))
(defn button-view
[{:keys [clicks]}]
[:root> {::z/on {:click on-click}}
[:button (str "Clicked " clicks " times")]])
(zc/reg-components
:incrementing-button {:view button-view
:props {:clicks clicks*}})
Here we have bound the clicks
prop to an atom. Similar to reagent, when that atom updates, our incrementing-button
component re-renders.
Zero also provides facilities for state management that resemble re-frame.
(ns increment-counter.client
(:require [zero.core :as z]
[zero.config :as zc]
[zero.component]
[zero.extras.db :as db]))
;; Init db value
(db/patch! [{:path [:clicks] :value 0}])
(defn button-view
[{:keys [clicks]}]
[:root> {::z/on {:click (z/act [::db/patch [{:path [:clicks]
:fn inc}]])}}
[:button (str "Clicked " clicks " times")]])
(zc/reg-components
:incrementing-button {:view button-view
:props {:clicks (z/bnd ::db/path [:clicks])}})
Here we have an in-memory database for our application. We bind our clicks prop to a path in the database, and then use the ::db/patch
effect to update the value at the :clicks
path.
Zero aims to be both simple and easy. It gives you options to follow familiar patterns for state management. But it also gives you the flexibility to manage state as you need to.
For more details, check out the User's Guide. The SSR Demo application provides further examples of Zero's state management.
Depends on modern browser APIs, works on the latest versions of all major browsers... but will break in some not-so-old versions. In particular, it depends on:
Check out the Zero User's Guide for a (reasonably) complete guide. Also, there are a few demos:
Feel free to DM me @Ray Stubbs in the Clojurians Slack for any questions.
Can you improve this documentation? These fine people already did:
Ray Stubbs & ThomasEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close