# 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. ## 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_click=dec, style={"flex": 1}), pn.Button("+", on_click=inc, style={"flex": 1}), style={"spacing": 8}, ), pn.Button("Reset", on_click=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_click=lambda: dispatch("dec")), pn.Button("+", on_click=lambda: dispatch("inc")), style={"spacing": 8}, ), pn.Button("Reset", on_click=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).