Build a Counter Widget

Open In Colab

This example demonstrates how to synchronize model state between the widget frontend and Python kernel with anywidget.

The render function creates a <button> element and registers an event handler to increment the model value when the button is clicked. A second event handler is registered to update the text output each time value changes on the model.

import anywidget
import traitlets


class CounterWidget(anywidget.AnyWidget):
    _esm = """
    function render({ model, el }) {
      let count = () => model.get("value");
      let btn = document.createElement("button");
      btn.classList.add("counter-button");
      btn.innerHTML = `count is ${count()}`;
      btn.addEventListener("click", () => {
        model.set("value", count() + 1);
        model.save_changes();
      });
      model.on("change:value", () => {
        btn.innerHTML = `count is ${count()}`;
      });
      el.appendChild(btn);
    }
    export default { render };
    """
    _css = """
    .counter-button {
      background-image: linear-gradient(to right, #a1c4fd, #c2e9fb);
      border: 0;
      border-radius: 10px;
      padding: 10px 50px;
      color: white;
    }
    """
    value = traitlets.Int(0).tag(sync=True)


w = CounterWidget()
w.value = 60

w

By treating the model as the source of truth, whether Python kernel or JavaScript update value, the count displayed is correct. Additionally, a single model serves as the source of truth for all views of that model. Therefore when w is displayed in another cell, the view is synchronized with the the output cell above.

w

But the state of a new widget instance is independent,

CounterWidget()