| name | reagami |
| description | Reagami UI library patterns for Epupp Squint components. Use when: writing or modifying Reagami components, hiccup UI in popup.cljs or panel.cljs, event handlers with Uniflow dispatch, :on-render lifecycle hooks, list rendering with keys, or comparing Reagami to Reagent. Covers component patterns, class shorthand, and Epupp-specific examples. IMPORTANT: Also load when planning UI changes that use Reagami. |
Reagami
Reagami is a minimal React-like UI library for Squint/ClojureScript, inspired by React's component model but simplified for direct DOM manipulation without a virtual DOM.
Reagami is not Reagent
Key differences from Reagent:
- No fragment
:<> - use wrapping divs instead
- No ratoms - use Uniflow for state management
- Components are plain functions returning hiccup vectors
Component Patterns
Basic Component
Components are functions returning hiccup vectors:
(defn greeting [{:keys [name]}]
[:div.greeting
[:h1 "Hello, " name "!"]])
Components with Children
Pass children as part of the props or use nested hiccup:
(defn card [{:keys [title]} & children]
[:div.card
[:h2 title]
[:div.card-body children]])
;; Usage
[card {:title "My Card"}
[:p "Card content here"]]
Event Handlers
Use :on-click, :on-input, etc:
(defn button [{:keys [on-click label]}]
[:button {:on-click on-click} label])
(defn input-field [{:keys [value on-change]}]
[:input {:type "text"
:value value
:on-input (fn [e] (on-change (.. e -target -value)))}])
Conditional Rendering
Use when and if:
(defn status-badge [{:keys [status]}]
[:div
(when status
[:span.badge {:class (when (= status "error") "badge-error")}
status])])
Lists with Keys
Use ^{:key ...} metadata for list items:
(defn item-list [{:keys [items]}]
[:ul
(for [item items]
^{:key (:id item)}
[:li (:name item)])])
Class Shorthand
Use .class syntax in element keywords:
[:div.container.main
[:span.label "Text"]]
;; Equivalent to:
[:div {:class "container main"}
[:span {:class "label"} "Text"]]
Dynamic Classes
Combine static and dynamic classes:
[:div.item {:class (str (when active "active ")
(when selected "selected"))}
content]
Event Handling
Reagami components dispatch actions via Uniflow. See ../../../dev/docs/architecture/uniflow.md for the full event system documentation.
;; In components, call the module's dispatch! function
[:button {:on-click #(dispatch! [[:editor/ax.save-script]])} "Save"]
;; Multiple actions in one dispatch
[:button {:on-click #(dispatch! [[:editor/ax.clear-results]
[:editor/ax.check-scittle]])} "Reset"]
Nested Components
Call components as vector first elements:
(defn parent [{:keys [items]}]
[:div
(for [item items]
^{:key (:id item)}
[child-component item])]) ; Note: [component props] not (component props)
Common Patterns from Epupp
Port Input Component
(defn port-input [{:keys [id label value on-change]}]
[:span
[:label {:for id} label]
[:input {:type "number"
:id id
:value value
:min "1"
:max "65535"
:on-input (fn [e] (on-change (.. e -target -value)))}]])
Item with Actions
Components use Uniflow dispatch for state changes:
(defn script-item [{:keys [script/name script/enabled] script-id :script/id}]
[:div.script-item
[:div.script-info
[:span.script-name name]]
[:div.script-actions
[:input {:type "checkbox"
:checked enabled
:on-change #(dispatch! [[:toggle-script script-id]])}]
[:button.delete {:on-click #(dispatch! [[:delete-script script-id]])}
"Delete"]]])
The :on-render Hook
Reagami provides an :on-render hook for lifecycle management. It fires on mount, update, and unmount.
Signature: (fn [node lifecycle data] ...)
node - the DOM node
lifecycle - one of :mount, :update, or :unmount
data - return value from previous invocation (for carrying state across lifecycles)
[:div {:on-render (fn [node lifecycle data]
(case lifecycle
:mount
(do
;; Element just added to DOM
(.add (.-classList node) "entering")
(js/requestAnimationFrame
#(.remove (.-classList node) "entering"))
nil)
:update
;; Element re-rendered (can track update count via data)
data
:unmount
;; Element being removed from DOM
;; NOTE: Too late to delay removal - element is already leaving
nil))}
content]
Use cases:
- Mount animations (add class on mount, remove after 1 frame to trigger CSS transition)
- Third-party library integration (initialize on mount, cleanup on unmount)
- Tracking update counts via returned data
Limitation for exit animations: By the time :unmount fires, Reagami has already decided to remove the element. For exit animations that need to delay removal, use state-level patterns (like list-watchers) instead.
See Reagami README for more details.