Interact — text template
Inside an
```interactblock, declare a plain-text output template withtemplate stl:— embed control values into STL or any text and display them live. The "output side" of Interact controls — the most common and simplest template type.
When to use
- ✅ Lightweight calculators (substitute slider/input values into a text result)
- ✅ Configuration preview ("you selected = ..." live echo)
- ✅ STL node dynamic generation (drag a slider, change a node's confidence / description)
- ✅ Teaching formula echo (input → formula → result)
- ❌ Need a chart → use Vega-Lite template
- ❌ Need animation / SVG → use SVG template
- ❌ Complex layout / multiple sections → split into multiple interact blocks or use Layout
Basic syntax
```interact
slider x range=0..10 step=1 default=5 label="x"
template stl:
[Selected value] -> [{x}]
```
template <type>: declares the template type + colon. Everything after the colon is template content. {var} is a placeholder.
Renders: a slider with a live STL line below: [Selected value] -> [5]. Drag → number changes.
Template types
| Type | Use | Doc |
|---|---|---|
template stl: |
Output STL statements / arbitrary plain text | This page |
template vega-lite: |
Output a live chart (Vega-Lite spec) | Interact + Vega-Lite |
template svg: |
Output an SVG scene | SVG scenes |
The "stl" type name is historical — it doesn't only output STL, it can output any text. The
[A] -> [B]in the template is just text; it isn't actually parsed into STL nodes (unless you later feed the rendered output into STG).
Placeholder syntax {...}
Inside the template, { } wraps an expression that's evaluated live.
1. Direct variable reference
template stl:
[Weight] -> [{weight}]
[Name] -> [{name}]
[Active] -> [{enabled}]
2. Expressions
template stl:
[BMI] -> [{weight / (height * height)}]
[Discounted price] -> [{price * (1 - discount)}]
The mini DSL supports (see Computed for full details):
- Arithmetic:
+ - * / % **(alsopow(a, b)) - Comparison:
== != < > <= >= - Logical:
&& || ! - Ternary:
cond ? a : b - Functions:
sqrt(x)abs(x)min(...)max(...)sin(x)cos(x)tan(x)log(x)exp(x)floor(x)ceil(x)round(x[, d])— full list in Computed - Constants:
pietauinfnan
3. Number formatting
There is no format-spec syntax — no Python-style f-string suffix (colon + precision + type letter) after the expression inside {}. A placeholder holds exactly one expression — nothing after it.
Numbers auto-format on output:
- Integers render plain:
{5}→5 - Floats render at ~6 significant digits:
{1/3}→0.333333
For a fixed number of decimal places, call round(expr, N) — that's the only mechanism:
template stl:
[BMI] -> [{round(bmi, 1)}] ← 1 decimal place
[Price] -> [${round(price, 2)}] ← 2 decimals (money)
[Percent] -> [{round(rate * 100, 1)}%] ← convert to percentage
[Int] -> [{round(count, 0)}] ← force whole number
There is no scientific-notation or alignment/width formatting — only round(expr, N) (and, upstream of it, ordinary arithmetic).
Examples
Example 1: pure echo
```interact
slider x range=0..100 step=1 default=50 label="x"
template stl:
[Current value] -> [{x}]
```
Renders: slider + one line "Current value = 50"; live update on drag.
Example 2: multi-line STL output + rounding
```interact
slider weight range=40..120 step=1 default=70 label="Weight (kg)"
slider height range=1.4..2.1 step=0.01 default=1.7 label="Height (m)"
template stl:
[Weight] -> [{weight} kg]
[Height] -> [{round(height, 2)} m]
[BMI] -> [{round(weight / (height * height), 1)}] ::mod(category="{(weight / (height * height)) < 18.5 ? 'Underweight' : (weight / (height * height)) < 24 ? 'Normal' : (weight / (height * height)) < 28 ? 'Overweight' : 'Obese'}")
```
Renders: 3 lines of live STL output. Line 3's category uses nested ternaries (single-quoted string literals, since the whole placeholder already sits inside a double-quoted ::mod() value) to display BMI category. For repeated expressions, refactor to computed derived values.
Example 3: free text template (not STL)
```interact
input name default="Alice" label="Name"
slider age range=1..100 step=1 default=30 label="Age"
select role options=Student,Engineer,Teacher,Other default=Student label="Role"
template stl:
Hello, {name}!
You're a {age}-year-old **{role}**.
Next year you'll be {age + 1}.
```
Renders: a free-text template (not constrained to STL form). The "stl" type name is really "plain text."
Example 4: configuration preview
```interact
input projectName default="my-app" label="Project name"
select stack options=Next.js,Astro,SvelteKit default=Next.js label="Stack"
toggle typescript default=true label="TypeScript"
toggle eslint default=true label="ESLint"
template stl:
**Project config preview**:
```bash
npx create-{stack} {projectName}{typescript == 'true' ? " --typescript" : ""}{eslint == 'true' ? " --eslint" : ""}
```
```
Renders: form → live-assembled command line. toggle yields the string "true"/"false", so it's compared with == 'true' — the ternary appends flags conditionally.
Plain-text fallback behavior
```interact blocks render as code blocks in non-supporting readers. Template content preserves {var} literals — readers see the template source + control declarations, fully understanding the original document's intent.
```interact
slider x range=0..10 step=1 default=5 label="x"
template stl:
[Selected value] -> [{x}]
```
→ Plain-text reader sees the above (with {x} placeholder), knows "x is a dynamic value here."
| Reader | Render |
|---|---|
| Rho | Full interaction + template live-evaluation |
| GitHub web | Code block ({x} literal visible) |
| Obsidian | Same |
| VS Code default preview | Same |
| cat / less | Plain text |
Common pitfalls
1. Reference an undeclared variable
```interact
slider x range=0..10 step=1 default=5 label="x"
template stl:
[Result] -> [{y}] ← ❌ y not declared
```
→ Template outputs {y} literal. All variables in template must first be declared via slider/input/select/toggle/chip/computed.
2. Expecting a format spec
template stl:
[BMI] -> [{round(bmi, 1)}] ← ✅ 1 decimal, via round()
A tutorial or LLM used to writing Python f-strings will reflexively add a colon-plus-precision suffix after the expression (mirroring Python's own format-spec mini-language). That suffix has no meaning here — the parser expects the placeholder to contain nothing but the expression, so anything appended after it breaks parsing.
There is no format-spec character set (f/e/d/s/% suffixes don't exist). Precision is controlled entirely by round(expr, N) upstream of the placeholder.
3. Expressions with unescaped nested quotes
template stl:
[Label] -> [{name == "Alice" ? "Admin" : "User"}] ← ⚠️ works stand-alone, but breaks the moment the template itself is inside a double-quoted STL/JSON value
→ When a placeholder sits inside something that's already double-quoted (an ::mod(key="...") value, a vega-lite JSON string, an SVG attribute), the template's own " closes that outer quote early.
Fix: use single-quoted string literals inside the expression — {name == 'Alice' ? 'Admin' : 'User'} — or pre-compute in computed and reference the computed name.
4. Deep nesting with callout / layout
:::card
```interact
slider x range=0..10 step=1 default=5 label="x"
template stl:
[Selected] -> [{x}]
```
:::
✅ Legal. But the interact block's height changes with template output length. Keep template output line count stable or give the container a fixed min-height.
5. Template output contains markdown special chars
template stl:
[Selected] -> [{x}] ← `[` `]` are markdown link syntax
In practice, usually fine — Rho renders template output as a fenced code block result, not triggering markdown link parsing. But watch out for * _ and other emphasis chars in template output.
6. Trying to chart with stl template
template stl:
draw a bar chart: {...} ← ❌ stl template doesn't render charts
→ stl template outputs plain text only, no graphical rendering. For charts use template vega-lite: (see Interact + Vega-Lite).
7. Template output expects markdown rendering
template stl:
**bold** and *italic* ← ❌ These chars output as literals, not bold/italic
→ Template defaults to plain-text output, no further markdown rendering. Stick to plain text for safety.
See also
- Interact controls — Control declarations
- Computed — Derived values (avoid complex expressions in template)
- Interact + Vega-Lite chart — chart template
- SVG scenes — svg template
- Shared state (namespace)
- Plain-text fallback principle