# Input

> A controlled text field with icon, placeholder and password mode. A Pyzed component for Python desktop GUIs, with examples and its API.

Source: https://albakore.github.io/pyzed/library/input/

:::tip[In plain words]
A box where people type one line of text: a name, an email, a password.
:::

A themed single-line text field. It is **controlled**: it shows `value` and reports every keystroke to `on_input`, so you store the text in your state and pass it back in `view()`. `on_submit` runs on Enter.

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

**Code:**

```python
pz.column([
    ui.input("", on_input, placeholder="Project name"),
    ui.input("ada@example.com", on_input, icon="mail"),
    ui.input("hunter2", on_input, secure=True, icon="lock"),
], spacing=10, width=360)
```

## When to use

- For names, paths, search boxes, emails and passwords (`secure=True`).
- For multi-line text or code use [Editor](https://albakore.github.io/pyzed/library/editor/).
- Give it an `id` to focus it from code with `pz.Command.focus(id)`.

## Usage

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

class Form(pz.App):
    def __init__(self):
        self.name = ""

    def view(self):
        return ui.input(self.name, lambda text: setattr(self, "name", text),
                        placeholder="Project name", on_submit=self.create, id="name")

    def on_start(self):
        return pz.Command.focus("name")
```

## 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.
:::

### Content

```python
ui.input("", on_input, placeholder="Project name", width=260)
```

```python
ui.input("Pyzed", on_input, width=260)
```

```python
ui.input("ada@example.com", on_input, icon="mail", width=260)
```

```python
ui.input("hunter2", on_input, secure=True, icon="lock", width=260)
```

### Size

```python
ui.input("Small", on_input, size="sm", width=260)
```

```python
ui.input("Medium", on_input, size="md", width=260)
```

```python
ui.input("Large", on_input, size="lg", width=260)
```

### Style

```python
ui.input("Borderless", on_input, borderless=True, width=260)
```

```python
ui.input("SELECT * FROM users", on_input, mono=True, width=260)
```

```python
ui.input("Can't edit me", width=260)
```

### Validation

Show errors with [Field](https://albakore.github.io/pyzed/library/field/)'s `error=`, next to the input.

```python
ui.field("Email", ui.input("ada@", on_input, width=220), error="Enter a valid email")
```

### States and events

Focus, blur and keys come from the common events: `.on(focus=..., blur=..., key_down=...)`.

```python
ui.input("Locked", on_input, disabled=True, width=220)
```

```python
ui.input("Copy me, not edit me", on_input, readonly=True, width=220)
```

```python
ui.input("ada@", on_input, invalid=True, width=220)
```

```python
ui.input("pyzed", on_input, on_clear=on_clear, width=220)
```

```python
ui.input("12345678", on_input, max_length=8, width=220)
```

```python
ui.input("centered", on_input, align="center", width=220)
```

### Base widget

The `pz.*` widget underneath: no theme, every color and size is yours.

```python
pz.text_input("", placeholder="pz.text_input", on_input=on_input, width=260, padding=8,
              radius=8, border_color="#c678dd", focus_border_color="#e5c07b")
```

## Events

Input has **3 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 | Of | Handler gets | When |
| --- | --- | --- | --- |
| `on_input` | `ui.input`, `pz.text_input` | `(text)` | Callable that receives the new text on every keystroke. |
| `on_submit` | `ui.input`, `pz.text_input` | `()` | Message or callable sent when Enter is pressed. |
| `on_clear` | `ui.input` | `()` | Shows an ✕ while there is text; sent when it is clicked. |

**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 SignUp(pz.App):
    def __init__(self):
        self.email = ""
        self.sent = False

    def set_email(self, text):           # on_input: the new text on every keystroke
        self.email, self.sent = text, False

    def send(self):                      # on_submit: Enter
        self.sent = "@" in self.email

    def view(self):
        return pz.column([
            ui.input(self.email, self.set_email, placeholder="Email", icon="mail", id="email",
                     on_submit=self.send, on_clear=lambda: self.set_email(""),
                     invalid=bool(self.email) and "@" not in self.email),
            ui.label("Thanks!" if self.sent else "Press Enter to send"),
        ], spacing=8, width=320)

    def on_start(self):
        return pz.Command.focus("email")
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class Typed:
    text: str

class SignUp(pz.App):
    email = ""
    sent = False

    def update(self, message):
        match message:
            case Typed(text):
                self.email, self.sent = text, False
            case "send":
                self.sent = "@" in self.email
            case "clear":
                self.email = ""

    def view(self):
        return ui.input(self.email, Typed, placeholder="Email", icon="mail",
                        on_submit="send", on_clear="clear")
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    email: str = ""
    sent: bool = False

def view(state, set_state):
    valid = "@" in state.email
    return pz.column([
        ui.input(state.email, lambda text: set_state(email=text, sent=False), placeholder="Email",
                 on_submit=lambda: set_state(sent=valid), invalid=bool(state.email) and not valid),
        ui.label("Thanks!" if state.sent else "Press Enter to send"),
    ], spacing=8, width=320)
```

## Properties and API

### `ui.input`

```python
input(
    value,
    on_input=None,
    *,
    placeholder='',
    icon=None,
    on_submit=None,
    on_clear=None,
    id=None,
    size='md',
    secure=False,
    width='fill',
    borderless=False,
    mono=False,
    disabled=False,
    readonly=False,
    invalid=False,
    max_length=None,
    align='start',
    theme=None,
)
```

A text field. Focus, blur and keys come from the common events: `.on(focus=..., blur=..., key_down=...)`.

| Property | Default | Description |
| --- | --- | --- |
| `value` | *required* | Current text. |
| `placeholder` | `""` | Hint text shown when empty. |
| `icon` | `None` | Name of a Lucide icon (`pyzed icons` lists them). |
| `id` | `None` | Stable identifier used to focus or scroll it with a `Command`. |
| `size` | `"md"` | `"sm"`, `"md"` or `"lg"`. |
| `secure` | `False` | Hides the text. |
| `width` | `"fill"` | Width: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `borderless` | `False` | No border or background (as in the command palette). |
| `mono` | `False` | Uses the monospace font (IBM Plex Mono). |
| `disabled` | `False` | Greys it out and ignores typing. |
| `readonly` | `False` | Selectable and copyable, but it can't be changed. |
| `invalid` | `False` | Paints the border in the error color. |
| `max_length` | `None` | Ignores edits longer than this. |
| `align` | `"start"` | Text alignment: `"start"`, `"center"` or `"end"`. |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |

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

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

### `pz.text_input`

```python
text_input(
    value,
    *,
    placeholder='',
    on_input=None,
    on_submit=None,
    id=None,
    size=None,
    font=None,
    padding=None,
    width=None,
    secure=False,
    icon=None,
    icon_size=None,
    bg=None,
    border_color=None,
    hover_border_color=None,
    focus_border_color=None,
    border_width=None,
    radius=None,
    color=None,
    placeholder_color=None,
    selection_color=None,
    icon_color=None,
    align_x=None,
)
```

A controlled text field: it shows `value` and notifies you of changes through `on_input`. For the themed field use `ui.input`.

| Property | Default | Description |
| --- | --- | --- |
| `value` | *required* | Current text. |
| `placeholder` | `""` | Hint text shown when empty. |
| `id` | `None` | Stable identifier used to focus or scroll it with a `Command`. |
| `size` | `None` | Font size. |
| `font` | `None` | Font family. |
| `padding` | `None` | Inner padding: `8`, `(vertical, horizontal)` or `(top, right, bottom, left)`. |
| `width` | `None` | Width: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `secure` | `False` | Hides the text (passwords). |
| `icon` | `None` | Icon character (use `pyzed.icons.glyph(name)`). |
| `icon_size` | `None` | Icon size. |
| `bg` | `None` | Background color (`"#rrggbb[aa]"`, `(r, g, b[, a])` or `"transparent"`). |
| `border_color` | `None` | Border color. |
| `hover_border_color` | `None` | Border color on hover. |
| `focus_border_color` | `None` | Border color when focused. |
| `border_width` | `None` | Border width in pixels. |
| `radius` | `None` | Corner radius: a number or `(top_left, top_right, bottom_right, bottom_left)`. |
| `color` | `None` | Text or icon color. |
| `placeholder_color` | `None` | Placeholder color. |
| `selection_color` | `None` | Selection color. |
| `icon_color` | `None` | Icon color. |
| `align_x` | `None` | Horizontal alignment: `"start"`/`"left"`, `"center"`, `"end"`/`"right"`. |

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

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

## Related

- [Search input](https://albakore.github.io/pyzed/library/search-input/): An input with a magnifying glass, for filtering.
- [Field](https://albakore.github.io/pyzed/library/field/): A settings row: title and description left, control right.
- [Number field](https://albakore.github.io/pyzed/library/number-field/): A number with −/+ buttons, limits, step and suffix.
