# Number field

> A number with −/+ buttons, limits, step and suffix. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/number-field/

:::tip[In plain words]
A box with a number and − / + buttons to make it smaller or bigger.
:::

A numeric field with − and + buttons. You can also type a value: `on_change` receives the number already clamped to `min`/`max`, and text that is not a number is ignored.

`ui.number_field` · Category: [Forms](https://albakore.github.io/pyzed/library/#forms) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#number_field)

**Code:**

```python
pz.row([
    ui.number_field(14, on_change, min=8, max=32, suffix="px"),
    ui.number_field(4, on_change, min=1, max=8),
    ui.number_field(0.5, on_change, step=0.1, suffix="s", width=140),
], spacing=12)
```

## When to use

- For exact values with sensible limits: font size, tab width, port, retries.
- Use `suffix` for units (`"px"`, `"ms"`, `"%"`).

## Usage

```python
import pyzed as pz
from pyzed import ui

class Settings(pz.App):
    def __init__(self):
        self.font_size = 14

    def view(self):
        return ui.number_field(self.font_size, lambda v: setattr(self, "font_size", v),
                               min=8, max=32, suffix="px")
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Options

```python
ui.number_field(4, on_change, min=1, max=8)
```

```python
ui.number_field(14, on_change, min=8, max=32, suffix="px")
```

```python
ui.number_field(0.5, on_change, step=0.1, suffix="s", width=140)
```

```python
ui.number_field(8080, on_change, min=1, max=65535, align="start", width=140)
```

### Limits

At a limit its button is dimmed and the value can't go further.

```python
ui.number_field(1, on_change, min=1, max=8)
```

```python
ui.number_field(8, on_change, min=1, max=8)
```

### States and events

Once clicked, ↑/↓ step it (Shift: ten steps) and Home/End jump to `min`/`max`.

```python
ui.number_field(9.5, on_change, step=0.5, prefix="$", decimals=2, width=150)
```

```python
ui.number_field(70000, on_change, invalid=True, width=150)
```

```python
ui.number_field(4, on_change, min=1, max=8, disabled=True)
```

## Events

Number field has **2 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

| Event | Handler gets | When |
| --- | --- | --- |
| `on_change` | `(value)` | Callable that receives the new value. |
| `on_submit` | `()` | Sent on Enter. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Settings(pz.App):
    def __init__(self):
        self.font_size = 14
        self.applied = 14

    def view(self):
        return pz.row([
            ui.number_field(self.font_size, lambda v: setattr(self, "font_size", v),   # on_change
                            min=8, max=32, suffix="px",
                            on_submit=lambda: setattr(self, "applied", self.font_size)),  # Enter
            pz.text("Preview", size=self.applied),
        ], spacing=12, align="center")
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class FontSize:
    value: float

class Settings(pz.App):
    font_size = 14

    def update(self, message):
        match message:
            case FontSize(value):
                self.font_size = value

    def view(self):
        return ui.number_field(self.font_size, FontSize, min=8, max=32, suffix="px")
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    font_size: float = 14

def view(state, set_state):
    return pz.row([ui.number_field(state.font_size, lambda v: set_state(font_size=v), min=8, max=32),
                   pz.text("Preview", size=state.font_size)], spacing=12, align="center")
```

## Properties and API

### `ui.number_field`

```python
number_field(
    value,
    on_change=None,
    *,
    min=None,
    max=None,
    step=1,
    width=120,
    suffix=None,
    prefix=None,
    align='center',
    decimals=None,
    disabled=False,
    invalid=False,
    id=None,
    on_submit=None,
    steppers=True,
    drag=True,
    drag_step=None,
    theme=None,
)
```

A numeric field with −/+ buttons that you can also drag, like Figma's. `on_change(value)` receives the number already clamped to `min`/`max`; anything that can't be parsed as a number is ignored. Press and move the mouse sideways over the number to change it (it keeps going outside the window); double-click it to type a value, and Enter, Escape or a click outside to finish. Once clicked, ↑/↓ step it (Shift: ten steps) and Home/End jump to `min`/`max`.

| Property | Default | Description |
| --- | --- | --- |
| `value` | *required* | Current value. |
| `min` | `None` | Minimum. |
| `max` | `None` | Maximum. |
| `step` | `1` | Button step. |
| `width` | `120` | Width: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `suffix` | `None` | Unit shown at the end (`"px"`, `"%"`…). |
| `prefix` | `None` | Text before the number (`"$"`…). |
| `align` | `"center"` | Where the number sits between the buttons: `"center"` (default), `"start"` or `"end"`. |
| `decimals` | `None` | Decimals shown (by default, as many as `step`). |
| `disabled` | `False` | Greys it out. |
| `invalid` | `False` | Paints the border in the error color. |
| `id` | `None` | Stable identifier used to focus or scroll it with a `Command`. |
| `steppers` | `True` | Shows the −/+ buttons (`False`: a compact field, as in a property grid). |
| `drag` | `True` | Lets the mouse change it by dragging (`False`: a plain text field). |
| `drag_step` | `None` | How much each pixel dragged adds (by default `step`, up to 1; steps under 1 take three pixels each). |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |

Events: `on_change`, `on_submit` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#number_field)

## Related

- [Slider](https://albakore.github.io/pyzed/library/slider/): Pick a number in a range by dragging.
- [Input](https://albakore.github.io/pyzed/library/input/): A controlled text field with icon, placeholder and password mode.
- [Field](https://albakore.github.io/pyzed/library/field/): A settings row: title and description left, control right.
