export const frontmatter = {
  title: 'Reactivity',
  description: 'Connect widgets with reactive inputs and outputs.'
};

Vibe Widgets stay synchronized with Python through a simple reactivity model. This page explains how
data flows between your notebook and your widgets and how to wire widgets together.

## The three primitives

| Primitive | Direction | Persistence | Use for |
| --- | --- | --- | --- |
| Input | Python → JS | State (sticky) | Data, configuration, selections from other widgets |
| Output | JS → Python | State (sticky) | User selections, computed values, filters |
| Action | Python → JS | Event (fire-once) | Commands like reset, focus, export |

### Inputs

Inputs are values that flow into your widget from Python. Use them for anything the widget needs to
display or respond to. Inputs are persistent: the widget holds onto the value until you change it.

```python
# Set an input from Python
widget.inputs.threshold = 0.5
widget.inputs.data = new_df # resets the data in the widget
```

You can also wire one widget's output to another widget's input:

<WidgetPreview src="/widgets/scatter_brush_linked.js" height={340} />

```python
# Slider selection flows into chart's highlight
chart = vw.create(
    "scatter plot with brush selection",
    df,
    outputs=vw.outputs(selected_indices="indices of selected points")
)

histogram = vw.create(
    "histogram with highlighted bars for selected data",
    vw.inputs(df, selected_indices=chart.outputs.selected_indices)
)
```

### Outputs

Outputs are values that flow out of your widget to Python. Use them for anything the widget
produces that Python might care about. Like inputs, outputs are persistent state.

```python
# Read the current selection
print(widget.outputs.selected_indices.value)

# React to changes
def on_selection(change):
    print(f"Selection changed: {change.new}")

widget.outputs.selected_indices.observe(on_selection)
```

### Actions

Actions are one-time commands from Python to the widget. Use them for behavior that should happen
once, not persist as state.

<WidgetPreview src="/widgets/scatter_actions.js" height={380} />

```python
widget = vw.create(
    "interactive scatter plot",
    df,
    actions=vw.actions(reset_view="Reset zoom and pan")
)

widget.actions.reset_view()
```

After an action fires, it's done. The widget doesn't "remember" that it was reset.

## Choosing the right primitive

Ask yourself: should this value stick around?

- Yes → Input or Output. Use input if the value comes from Python; use output if it comes from the widget.
- No → Action. Use an action if it's a one-time command.

If you try to model a one-time command as an input, it becomes sticky:

```python
# Avoid this
widget.inputs.should_reset = True
```

The widget would reset and keep resetting because the input is still true. Actions avoid that
behavior by firing once and not lingering.

## Wiring widgets together

The real power of this model shows up when you connect widgets:

<WidgetPreview src="/widgets/scatter_brush_linked.js" height={340} />

```python
scatter = vw.create(
    "scatter plot with brush selection tool",
    df,
    outputs=vw.outputs(selected_indices="indices of selected points")
)

histogram = vw.create(
    "histogram with highlighted bars for selected data",
    vw.inputs(df, selected_indices=scatter.outputs.selected_indices)
)
```

When you select points in the scatter plot, the histogram updates via trait syncing without requiring any additional runs. Outputs are
exposed under `widget.outputs.<name>`.
