-->
This guide provides examples and patterns for converting React components between JavaScript/JSX, Reagent (ClojureScript), and UIx (ClojureScript).
function MyComponent({ title, children }) {
return (
<div className="container">
<h1>{title}</h1>
{children}
</div>
);
}
// With hooks
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
(defn my-component [{:keys [title]} & children]
[:div.container
[:h1 title]
children])
;; With hooks (using with-let for state)
(defn counter []
(let [[count set-count] (react/useState 0)]
[:button {:on-click #(set-count (inc count))}
(str "Count: " count)]))
(defui my-component [{:keys [title children]}]
($ :div.container
($ :h1 title)
children))
;; With hooks
(defui counter []
(let [[count set-count] (uix.core/use-state 0)]
($ :button
{:on-click #(set-count (inc count))}
(str "Count: " count))))
<div className="parent">
<Child prop1="value1" prop2={value2}>
<span>Inner content</span>
</Child>
</div>
[:div.parent
[child {:prop1 "value1"
:prop2 value2}
[:span "Inner content"]]]
($ :div.parent
($ child
{:prop1 "value1"
:prop2 value2}
($ :span "Inner content")))
function Container({ style, className, children }) {
return (
<div style={style} className={className}>
{children}
</div>
);
}
// Usage
<Container style={{margin: 10}} className="wrapper">
<p>Content</p>
</Container>
(defn container [{:keys [style class]} & children]
[:div {:style style :class class}
children])
;; Usage
[container {:style {:margin 10}
:class "wrapper"}
[:p "Content"]]
(defui container [{:keys [style className children]}]
($ :div
{:style style
:className className}
children))
;; Usage
($ container
{:style #js {:margin 10}
:className "wrapper"}
($ :p "Content"))
function Example() {
const [count, setCount] = useState(0);
const ref = useRef(null);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <div ref={ref}>{count}</div>;
}
(defn example []
(let [[count set-count] (react/useState 0)
ref (react/useRef nil)]
(react/useEffect
(fn []
(set! (.-title js/document) (str "Count: " count))
js/undefined)
#js [count])
[:div {:ref ref} count]))
(defui example []
(let [[count set-count] (uix.core/use-state 0)
ref (uix.core/use-ref)]
(uix.core/use-effect
#(set! (.-title js/document) (str "Count: " count))
[count])
($ :div {:ref ref} count)))
function Button({ onClick }) {
return (
<button onClick={(e) => onClick(e.target.value)}>
Click me
</button>
);
}
(defn button [{:keys [on-click]}]
[:button {:on-click #(on-click (.. % -target -value))}
"Click me"])
(defui button [{:keys [onClick]}]
($ :button
{:onClick #(onClick (.. % -target -value))}
"Click me"))
<div
className={`base-class ${active ? 'active' : ''}`}
style={{
backgroundColor: color,
fontSize: size + 'px'
}}>
Content
</div>
[:div
{:class ["base-class" (when active "active")]
:style {:background-color color
:font-size (str size "px")}}
"Content"]
($ :div
{:className (str "base-class" (when active " active"))
:style #js {:backgroundColor color
:fontSize (str size "px")}}
"Content")
function Example() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
}
(defn example []
(let [input-ref (react/useRef nil)]
(react/useEffect
(fn []
(.focus @input-ref)
js/undefined)
#js [])
[:input {:ref input-ref}]))
(defui example []
(let [input-ref (uix.core/use-ref)]
(uix.core/use-effect
#(.focus @input-ref)
[])
($ :input {:ref input-ref})))
{isLoading ? <Spinner /> : <Content />}
{showMessage && <Message />}
(if is-loading
[spinner]
[content])
(when show-message
[message])
(if is-loading
($ spinner)
($ content))
(when show-message
($ message))
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
[:ul
(for [item items]
^{:key (:id item)}
[:li (:text item)])]
($ :ul
(for [item items]
($ :li {:key (:id item)}
(:text item))))
function Form() {
const [value, setValue] = useState("");
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
(defn form []
(let [[value set-value] (react/useState "")]
[:input
{:value value
:on-change #(set-value (.. % -target -value))}]))
(defui form []
(let [[value set-value] (uix.core/use-state "")]
($ :input
{:value value
:onChange #(set-value (.. % -target -value))})))
Component Definition
defn
with vector syntaxdefui
with $
macroElement Creation
$
macro with keywordsProps
Children
Hooks
useX
namingStyle
When converting from React:
When converting from Reagent:
$
macroGeneral Tips:
Props Spreading
{...props}
props
:& props
Ref Handling
useRef(null)
react/useRef(nil)
use-ref
Effect Cleanup
Class Names
Remember to always test thoroughly after conversion, as subtle differences between the frameworks can lead to unexpected behavior.
Add this context to your project via the
ctxs
command line integration: