Special Attributes
Some attributes have behavior specific to Retend elements. The most common are class, style, and ref.
The class Attribute
CSS classes are specified with the class attribute (unlike React, which uses className). A string of space-separated classes works, exactly as in standard HTML.
<div class="card p-4 shadow">Content goes here</div>
Dynamic Classes
Arrays and objects define classes conditionally. This avoids constructing class strings manually.
An array of strings can be passed, or objects where the key is the class name and the value is a boolean or a Cell. When a value is a Cell, Retend binds the class to that Cell and updates the class without rerendering the component.
import { Cell } from 'retend'; export function ToggleButton() { const isActive = Cell.source(false); const isInactive = Cell.derived(() => !isActive.get()); return ( <button class={[ 'btn', { 'btn-active': isActive, 'btn-inactive': isInactive, }, ]} onClick={() => isActive.set(!isActive.get())} > Toggle me </button> ); }
The style Attribute
The style attribute accepts either a standard CSS string or an object containing styles.
When an object is used, CSS property names are typically written in camelCase (e.g., backgroundColor instead of background-color).
<div style={{ color: 'var(--color-brand)', borderRadius: '8px' }}> Styled content </div>
As with class, style object values can be Cells. Retend updates the affected style when the Cell changes without rerendering the element.
The ref Attribute
The ref attribute provides a reference to the underlying UI element after it is created. It is used for imperative actions, focus management, or integration with non-reactive third-party libraries.
A ref is created with Cell.source(null) and passed to the ref attribute. Retend sets the cell to the element after it is created.
import { Cell } from 'retend'; export function AutoFocusInput() { // 1. Create a reference starting at null const inputRef = Cell.source(null); const handleFocus = () => { // 3. Access the element safely using .get() inputRef.get()?.focus(); }; return ( <div class="flex gap-2"> <input ref={inputRef} // 2. Attach the reference type="text" placeholder="Type here..." /> <button onClick={handleFocus}>Focus the input</button> </div> ); }
Example: Media Control
Refs interact with browser APIs that Retend does not wrap reactively, such as APIs for controlling <video> or <audio> elements.
import { Cell } from 'retend'; export function VideoPlayer() { const videoRef = Cell.source<HTMLVideoElement | null>(null); const togglePlay = () => { const video = videoRef.get(); if (video) { if (video.paused) video.play(); else video.pause(); } }; return ( <div class="video-container"> <video ref={videoRef} src="/promo.mp4" /> <button onClick={togglePlay}>Play/Pause</button> </div> ); }
Ref Forwarding
ref={cell} is a prop. It can be passed to a child component and attached to an element in that component.
import { Cell, onSetup } from 'retend'; function MyInput(props) { return <input ref={props.ref} class="custom-input" />; } export function ParentComponent() { const inputRef = Cell.source(null); onSetup(() => { inputRef.get()?.focus(); }); return <MyInput ref={inputRef} />; }
Fragment refs
A Fragment ref stores the nodes that a Fragment renders. A Fragment ref is useful for inspecting or processing multiple nodes without adding a wrapper element.
A Fragment ref is created as follows:
- Create a source cell with an initial value of
null. - Pass the cell to the Fragment's
refattribute. - Read the rendered nodes with the cell's
get()method.
import { Cell, Fragment } from 'retend'; export function MessageGroup() { const nodesRef = Cell.source<Node[] | null>(null); const logNodes = () => { console.log(nodesRef.get()); }; return ( <> <Fragment ref={nodesRef}> <p>First message</p> <p>Second message</p> </Fragment> <button onClick={logNodes}>Log messages</button> </> ); }
Retend updates the ref cell whenever the Fragment's rendered content changes. The cell contains:
- Each rendered element and text node, in document order.
- An empty array when the Fragment renders no nodes.
For example, the following ref changes when showSuccess changes:
import { Cell, Fragment, If } from 'retend'; export function StatusMessage() { const showSuccess = Cell.source(true); const nodesRef = Cell.source<Node[] | null>(null); return ( <> <Fragment ref={nodesRef}> {If( showSuccess, () => ( <p>Saved</p> ), () => ( <p>Save failed</p> ) )} </Fragment> <button onClick={() => showSuccess.set(!showSuccess.get())}> Toggle status </button> </> ); }
An element ref is used for one specific element rather than the complete Fragment output.