[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pythonnative/pythonnative/v0.43.1/docs/examples/counter.md [Back]  [Original]

# Counter

A counter with two buttons (increment and decrement), a reset, and a
small bit of conditional styling. Practical introduction to
[`use_state`][pythonnative.use_state] and event handlers.

Paste it into `app/main.py` of a project scaffolded with `pn init`,
then run `pn preview app.main.Counter`.

## The code

```python
import pythonnative as pn


@pn.component
def Counter(initial: int = 0):
    count, set_count = pn.use_state(initial)

    inc = lambda: set_count(count + 1)
    dec = lambda: set_count(count - 1)
    reset = lambda: set_count(initial)

    color = "#0a84ff" if count >= 0 else "#ff3b30"

    return pn.Column(
        pn.Text(
            f"Count: {count}",
            style={"font_size": 28, "bold": True, "color": color},
        ),
        pn.Row(
            pn.Button("-", on_press=dec, style={"flex": 1}),
            pn.Button("+", on_press=inc, style={"flex": 1}),
            style={"spacing": 8},
        ),
        pn.Button("Reset", on_press=reset),
        style={
            "spacing": 12,
            "padding": 16,
            "align_items": "stretch",
        },
    )
```

## Notable bits

- `inc`, `dec`, `reset` are simple closures that capture `count` and
  `initial`. They are recreated on every render but the reconciler
  doesn't care; only behavior, not identity, matters here.
- `style` arguments are plain dicts. Layout properties (`flex`,
  `spacing`, `padding`, `align_items`) sit beside visual properties
  (`color`, `font_size`, `bold`).
- The `Row` uses `flex: 1` on its children to split space evenly. The
  same `flex` knob works inside `Column`s for vertical layouts.

## Stable handlers (optional)

If you find yourself passing the handlers down to deeply nested
components and the renders are getting expensive, switch to
[`use_callback`][pythonnative.use_callback]:

```python
inc = pn.use_callback(lambda: set_count(count + 1), [count])
```

That keeps the function reference stable for any equal `count` and
gives memoized children a chance to skip re-rendering. For a small
top-level component like this one, the closure version is fine.

## Reducer flavor

For more complex counters (with bounds, multipliers, history), reach
for [`use_reducer`][pythonnative.use_reducer]:

```python
def reducer(state, action):
    if action == "inc":
        return state + 1
    if action == "dec":
        return state - 1
    if action == "reset":
        return 0
    raise ValueError(f"unknown action: {action!r}")


@pn.component
def Counter():
    state, dispatch = pn.use_reducer(reducer, 0)
    return pn.Column(
        pn.Text(str(state), style={"font_size": 28}),
        pn.Row(
            pn.Button("-", on_press=lambda: dispatch("dec")),
            pn.Button("+", on_press=lambda: dispatch("inc")),
            style={"spacing": 8},
        ),
        pn.Button("Reset", on_press=lambda: dispatch("reset")),
        style={"spacing": 12, "padding": 16, "align_items": "stretch"},
    )
```

The reducer is a plain function: easy to test, easy to read.

## Next steps

- Capture and submit user input: [Forms](forms.md).
- Render dynamic data: [Lists](lists.md).
- Move between screens: [Navigation](navigation.md).

Web Proxy Viewer  |  New URL  |  Original Page