Respo
Guide
API Docs
Community
GitHub
Respo: a Virtual DOM library in ClojureScript.
Newer version of Respo has been moved to calcit-js. This page is deprecating.
Create a element:
(div {:class-name "demo"
      :style {:color :red
              :font-size 16
              :font-family "Josefin Sans"}
      :on-click (fn [event dispatch! mutate!])})

; respo.core/div
Nested elements:
(div {}
  (span {})
  (div {}))

; respo.core/span
Add text nodes:
(div {}
  (<> "text")
  (<> "text with style" {:color :red}))

; respo.core/<>

Create Components

To define components, use defcomp, which is a cacro:
(defcomp comp-demo [p1 p2]
  (div {}
    (<> p1)
    (<> p2)))

(comp-demo :a :b)

; respo.core/defcomp
Use render! to mount a component. It also handles re-rendering if mounting already happened.
(defonce *store (atom {}))
(defn dispatch! [op op-data] (swap! *store assoc :a 1))

(render! mount-target (comp-container @*store) dispatch!)

; respo.core/render!
To hot replace app code, use render! function. clear-cache! for resetting internal rendering caches:
(defn reload! []
  (clear-cache!)
  (render! mount-target (comp-container @*store) dispatch!))

; respo.core/clear-cache!

Adding Effects

Define effects with defeffect macro. You may also compare arguments with old one to decide what to do:
(defeffect effect-focus [a] [action el at-place?]
  (when (= :mount action)
    (.focus (.querySelector el "input"))))
Pass component results in a vector with effects defined:
(defcomp comp-draft [data]
  [(effect-focus data)
   (div {}
      (input {})))]
The effect will be called with action in :mount :before-update :update and unmount.
Respo would compare [data] passed to effect-focus with old arguments and updates will be called when they change.

States Management

At component level, use it with an explicit states parameter passed from root states.
The idea is based on cursors, but implemented differently:
(defcomp comp-a [states]
  (let [cursor (:cursor states)
        state (or (:data states) {:a 1})]
    (div {}
      (<> (str "count" (:a state)))
      (button {:inner-text "Add"
               :on-click (fn [e d!]
                          (d! cursor (update state :a inc)))}))))
Respo uses an atom to maintain global states for smooth experiences of hot code swapping, might look tedious though:
(defonce *store (atom {}))
(defn dispatch! [op op-data] (swap! *store assoc :a 1))

(add-watch *store :changes
           (fn []
               (render! mount-target (comp-container @*store) dispatch!)))
Read more about cursor and >> in the docs. at component-level states.

Ecosystem

During developing Respo, a bunch of libraries are added:
  • ui -- basic UI styles collected based on Flexbox
  • markdown -- subset Markdown syntax rendering to virtual DOM
  • message -- displaying message on top-right corner
  • feather -- icons library of feather
  • alerts -- replacing alert/confirm/prompt components
  • router -- HTML5 router library decoupled from view part
  • reel -- time travelling developing tool
  • value -- to display collections
  • form -- simple form library
  • notifier -- notification notifier
  • You may also try Reacher which is a React wrapper.

    Try Respo

    Now it's your turn to read Guide and try Respo:
    For Advanced developers, probably the best way to understand Respo is to read code of how the author is using it. Contact me on Twitter anytime if you got questions.
    Send feedbacks on issues if you want to improve this page. Old versions.