Shared state (namespace)
By default each
```interactblock owns its own state independently — dragging one block's slider doesn't affect another. Usenamespace <name>to make multiple interact blocks share the same variables — declare sliders / computed once; reuse across many blocks.
When to use
- ✅ One set of sliders driving multiple outputs (e.g., 3 sliders for principal / rate / years; both a text block and a chart block use them)
- ✅ Multiple charts interspersed with prose (write a paragraph, place a chart, write more, place another — all using the same parameters)
- ✅ Layout / Tabs cards sharing data (left card has sliders, right card has the chart)
- ✅ Avoid duplicate declarations (5 interact blocks all need the same slider — writing it 5 times is a maintenance nightmare)
- ❌ Two genuinely independent calculators (two BMI calculators that shouldn't interfere — don't share namespace)
- ❌ As a "global variable bag" (a namespace should focus on a single context, not collect all variables)
Basic syntax
Add namespace <name> as the first line inside the fence:
```interact
namespace finance
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
slider years range=1..30 step=1 default=10 label="years"
```
Some prose...
```interact
namespace finance
template stl:
[Rate] -> [{round(rate*100, 1)}%]
[Years] -> [{years}]
```
Renders: block 1 has 2 sliders; block 2 has no sliders (it just reuses the namespace's), only template output. Drag block 1's slider → block 2's output updates live.
namespace naming rules
- ASCII letters + digits + underscores
- Case-sensitive (
finance≠Finance) - Recommended semantic names (
finance/physics/bmi/chart_demo) - Doesn't share across pages — namespace is per-page only (close the page, state is gone)
Examples
Example 1: parameters + outputs + chart all sharing
**Compound interest calculator** — drag sliders to see changes.
```interact
namespace finance
slider principal range=1000..100000 step=1000 default=10000 label="principal"
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
slider years range=1..30 step=1 default=10 label="years"
computed compound = principal * pow(1+rate, years)
computed gain = compound - principal
```
Numerical results:
```interact
namespace finance
template stl:
[Principal] -> [${round(principal)}]
[Final value] -> [${round(compound)}]
[Net gain] -> [${round(gain)}]
```
Growth curve:
```interact
namespace finance
template vega-lite:
{
"data": {"sequence": {"start": 0, "stop": {years}, "step": 1, "as": "year"}},
"transform": [{"calculate": "{principal} * pow(1+{rate}, datum.year)", "as": "value"}],
"mark": "line",
"encoding": {"x": {"field": "year"}, "y": {"field": "value", "type": "quantitative"}}
}
```
3 blocks share the finance namespace. Block 1 declares sliders + computeds; blocks 2 and 3 are template-only, reusing all variables. Dragging block 1's sliders drives blocks 2 and 3 live.
Example 2: Layout + namespace combo
```layout grid cols=2
:::card accent=blue
**Inputs**
```interact
namespace bmi
slider weight range=40..120 step=1 default=70 label="weight"
slider height range=1.4..2.1 step=0.01 default=1.7 label="height"
computed bmi_value = weight / (height * height)
template stl:
[Weight] -> [{weight} kg]
[Height] -> [{round(height, 2)} m]
```
:::
:::card accent=green
**Result**
```interact
namespace bmi
template stl:
[BMI] -> [{round(bmi_value, 1)}]
[Category] -> [{bmi_value < 18.5 ? "Underweight" : bmi_value < 24 ? "Normal" : "Overweight"}]
```
:::
```
Layout provides visual organization; namespace provides state sharing — two orthogonal concerns working together.
Example 3: cross-section shared parameters
## Financial simulation
Let's look at simple retirement planning. First, set parameters:
```interact
namespace retire
slider currentAge range=20..60 step=1 default=30 label="current age"
slider retireAge range=50..75 step=1 default=65 label="retire age"
slider monthly range=100..5000 step=100 default=1000 label="monthly investment"
slider annualRate range=0..0.1 step=0.005 default=0.05 label="annual rate"
computed years = retireAge - currentAge
computed total = monthly * 12 * years * pow(1 + annualRate/12, 12*years)
```
### Numerical results
```interact
namespace retire
template stl:
[Current age] -> [{currentAge}]
[Retire age] -> [{retireAge}]
[Investment period] -> [{years} years]
[Principal at retirement] -> [${round(total)}]
```
### Capital growth
```interact
namespace retire
template vega-lite: {...}
```
### Key insight
You can see, raising `monthly` by $100 raises retirement principal by $... (live).
**Start early** + **invest consistently** > investing more in a short window.
The whole section's 4 interactive blocks share one namespace — readers drag one slider and the entire section's numbers + chart + commentary all update.
Example 4: multiple isolated namespaces
**Compare two investment plans** — A and B don't interfere.
Plan A:
```interact
namespace planA
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
slider years range=1..30 step=1 default=10 label="years"
template stl:
[A final] -> [${round(1000 * pow(1+rate, years))}]
```
Plan B:
```interact
namespace planB
slider rate range=0..0.15 step=0.01 default=0.08 label="rate"
slider years range=1..30 step=1 default=15 label="years"
template stl:
[B final] -> [${round(1000 * pow(1+rate, years))}]
```
Readers can see both plans simultaneously under different parameters.
Two namespaces are isolated — A's rate and B's rate are completely independent.
Plain-text fallback behavior
namespace shows as a code-block literal in non-supporting readers — readers see namespace finance on its own line inside the fence and understand the original intended cross-block sharing.
```interact
namespace finance
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
```
| Reader | Render |
|---|---|
| Rho | Full cross-block state sharing |
| GitHub web | Code block (namespace literal visible) |
| Obsidian | Same |
| VS Code default preview | Same |
| cat / less | Plain text |
The namespace name is self-documenting in source — readers seeing namespace finance understand the related blocks belong to the same context.
Common pitfalls
1. Forgot namespace → state isolated
```interact
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
```
```interact
template stl: [Rate] -> [{rate}] ← ❌ rate not declared in block 2
```
→ Block 2 throws "undefined rate" or shows {rate} literal. Both blocks need the same namespace to share:
```interact
namespace foo
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
```
```interact
namespace foo
template stl: [Rate] -> [{rate}] ← ✅
```
2. namespace case mismatch
```interact
namespace Finance
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
```
```interact
namespace finance ← ❌ Different case → not shared
template ...
```
→ namespace is case-sensitive. Finance ≠ finance.
3. Same variable redeclared in same namespace
```interact
namespace foo
slider rate range=0..0.15 step=0.01 default=0.05 label="rate"
```
```interact
namespace foo
slider rate range=0..0.20 step=0.05 default=0.10 label="rate" ← ❌ Re-declares rate
```
→ Second declaration is undefined behavior (may overwrite, may error, may keep the first). Each variable in a namespace is declared once — subsequent blocks just reference, not redeclare.
4. namespace abuse → "everything global"
```interact
namespace shared
slider a range=0..10 step=1 default=5 label="a"
slider b range=0..10 step=1 default=5 label="b"
slider c range=0..10 step=1 default=5 label="c"
... (40 sliders)
```
→ One namespace overstuffed → readers can't tell which blocks relate. A namespace should focus on a single context (one calculator / one section's parameter panel). Multiple contexts → multiple namespaces.
5. Cross-page persistence expectation
<!-- on page A: -->
```interact
namespace foo
slider x range=0..10 step=1 default=5 label="x"
```
<!-- on page B: -->
```interact
namespace foo
template stl: [{x}] ← ❌ x not declared in B; namespace doesn't cross pages
```
→ namespace is per-page only — when you navigate to B, A's foo namespace is gone. Each page's namespace is independent.
6. Nesting doesn't break namespace isolation
```layout
:::card
```interact
namespace foo
slider x range=0..10 step=1 default=5 label="x"
```
:::
:::card
```interact
namespace bar
template stl: [{x}] ← ❌ Different namespaces; can't see foo's x
```
:::
```
→ Nesting doesn't affect namespace rules — namespace is the isolation boundary, independent of nesting. To share across cards, both interact blocks need the same namespace.
Best practices
1. Semantic namespace names
✅ finance / bmi_calc / physics_demo / retire_planning
❌ ns1 / tmp / data / state
2. Declare in the first block, reference later
```interact
namespace foo
slider a range=0..10 step=1 default=5 label="a"
slider b range=0..10 step=1 default=5 label="b"
computed sum = a + b
```
```interact
namespace foo
template stl: [{sum}] ← Template only, no redeclare
```
3. computed in the first block
Put computed in the same first block as the sliders — later blocks reference computed names directly, no duplicating expressions:
```interact
namespace bmi
slider weight range=40..120 step=1 default=70 label="weight"
slider height range=1.4..2.1 step=0.01 default=1.7 label="height"
computed bmi_value = weight / (height * height)
computed category = bmi_value < 18.5 ? "thin" : "normal"
```
```interact
namespace bmi
template stl: [{round(bmi_value, 1)}] [{category}]
```
4. Scope your namespace
A namespace covers one section / one demo — don't let a namespace span the entire document. Multi-section docs → multiple namespaces.
See also
- Interact controls — Control declarations
- Interact text template — Template variable references
- Computed — Derived values inside namespace
- Nested DSL — Namespace and nesting are orthogonal
- Plain-text fallback principle