JS Effects
JS effects let you wire components together with reactive JavaScript expressions that run entirely on the frontend. When a component property changes, the expression re-runs automatically, with no round-trip to MATLAB, avoiding waiting for drawnow events and keeping MATLABs thread free for heavy computations.
Writing an effect
The ic.mixin.Effectable.jsEffect method is available on every component. You pass it one or more other components, followed by a JavaScript arrow function as a string. The first parameter of the arrow function is always the calling component; the rest correspond to the additional components you passed in.
In the demo below, the effect reads the slider value, updates the bar, and switches the bar’s color variant based on the value:
slider = ic.Slider("Value", 30);
bar = ic.ProgressBar();
slider.jsEffect(bar, "(s, b) => {" + newline + ...
" const v = s.props.value;" + newline + ...
" b.props.value = v;" + newline + ...
" b.props.variant = v < 30 ? 'destructive' : v < 70 ? 'primary' : 'success';" + newline + ...
"}");The expression runs inside the frontend’s reactivity system. Any property you read from props is tracked: when it changes, the entire expression re-runs. Any property you write to props is pushed back to MATLAB through the normal property channel.
Multiple sources
An effect can depend on any number of components. Pass them all as arguments before the expression, and they become available as parameters in the arrow function.
Here, two sliders feed into a label that shows their sum:
sliderA = ic.Slider("Value", 0, "Max", 50);
sliderB = ic.Slider("Value", 0, "Max", 50);
total = ic.Label();
sliderA.jsEffect(sliderB, total, ...
"(a, b, t) => { t.props.text = String(a.props.value + b.props.value); }");The component proxy
Inside an effect expression, each component parameter is a proxy object with these properties:
| Property | Description |
|---|---|
props | Reactive properties (read triggers re-run, write pushes to MATLAB) |
methods | Callable component methods |
el | The component’s root DOM element |
id | The component’s unique ID |
type | The MATLAB class name (e.g. "ic.Slider") |
comp.jsEffect(other, "(c, o) => {" + newline + ...
" const val = c.props.value;" + newline + ...
" o.methods.focus();" + newline + ...
" c.el.style.opacity = val > 50 ? '1' : '0.5';" + newline + ...
"}"); Reading from props is tracked: the effect will re-run whenever that property changes. Writing to props updates the frontend state and synchronizes back to MATLAB through the normal property channel. Calling a method via methods is not tracked (it will not cause re-runs).
Property names in the proxy use camelCase (the frontend convention), not PascalCase. So props.value instead of props.Value, props.text instead of props.Text, and so on.
DOM event listeners
Effects can attach event listeners to a component’s DOM element via the el property. This lets you react to low-level browser events (click, hover, scroll) without going through MATLAB’s event system.
btn = ic.Button("Label", "Click me");
counter = ic.Label("Text", "0");
btn.jsEffect(counter, ...
"(b, c) => { b.el?.addEventListener('click', () => {" + newline + ...
" c.props.text = String(Number(c.props.text) + 1);" + newline + ...
"}); }");Calling methods
The methods object lets you invoke reactive methods on other components. For example, you can focus an editor when a button is clicked:
editor.jsEffect(btn, ...
"(ed, b) => { b.el?.addEventListener('click', () => ed.methods.focus()); }"); Methods called through the proxy behave exactly like calling them from MATLAB, except there is no MATLAB round-trip: the call happens instantly on the frontend.
Removing effects
jsEffect returns a ic.effect.JsEffect handle. Call remove() on it to tear down the reactive connection:
e = slider.jsEffect(bar, "(s, b) => { b.props.value = s.props.value; }");
% later, tear down the connection
e.remove(); When the handle goes out of scope or is deleted, the effect is removed automatically. If you need the effect to persist, store the handle in a property or a variable that stays in scope.
The bind shorthand
For the common case of piping one property to another, ic.utils.bind is a shorthand that builds the jsEffect for you. It takes a source component, a source property name, a target component, and a target property name:
slider = ic.Slider("Value", 40);
progress = ic.ProgressBar();
ic.utils.bind(slider, "value", progress, "value");An optional fifth argument is a JavaScript expression that transforms the source value before assigning it. Use x as the placeholder:
slider = ic.Slider("Value", 50);
label = ic.Label();
invertedBar = ic.ProgressBar();
% append a percent sign
ic.utils.bind(slider, "value", label, "text", "x + '%'");
% invert the value
ic.utils.bind(slider, "value", invertedBar, "value", "100 - x");The transform is a plain JavaScript expression: arithmetic, string concatenation, ternary operators, Math functions. When you need anything more complex (conditionals, multiple writes, side effects), use jsEffect directly.