Shared state (namespace)

By default each ```interact block owns its own state independently — dragging one block's slider doesn't affect another. Use namespace <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 (financeFinance)
  • 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. Financefinance.

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_planningns1 / 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

Open in Rho MD →