Special Attributes

Some attributes have behavior specific to Retend elements. The most common are class, style, and ref.

The class Attribute

To specify CSS classes on an element, use the class attribute (unlike React, which uses className). You can pass a string of space-separated classes, exactly as you would in standard HTML.

<div class="card p-4 shadow">Content goes here</div>

Dynamic Classes

Use arrays and objects to define classes conditionally. This avoids constructing class strings manually.

You can pass an array of strings, 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 your styles.

When using an object, CSS property names should typically be 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. Use it for imperative actions, focus management, or integration with non-reactive third-party libraries.

Create a ref with Cell.source(null) and pass the cell 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

Use refs to 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. Pass it to a child component and attach it 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. Use a Fragment ref when you need to inspect or process multiple nodes without adding a wrapper element.

To create a Fragment ref:

  1. Create a source cell with an initial value of null.
  2. Pass the cell to the Fragment's ref attribute.
  3. 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>
    </>
  );
}

Use an element ref when you need one specific element instead of the complete Fragment output.