---
title: Combobox
url: "https://native-sdk.dev/docs/components/combobox"
docs_index: /llms.txt
lastUpdated: 2026-10-16
---

> For an index of all documentation, see [/llms.txt](/llms.txt).

Use `combobox` for a text-entry trigger with a menu affordance. `on-input` dispatches a `TextInputEvent` from `@native-sdk/core/text` in a TypeScript core, or `canvas.TextInputEvent` in a Zig core. Filter options in the model as the user types. Compose the options in an anchored [dropdown-menu](/docs/components/dropdown-menu), as with [select](/docs/components/select). Keep the trigger and menu in a `stack`, render the menu inside an `if`, and clear the open flag in `on-dismiss`.

Enter submits when `on-submit` is bound; otherwise it opens the picker. Space and the Up/Down arrow keys open the picker. Once focus enters the menu, Enter selects the focused `menu-item`.

## Markup

The model owns the query and the open flag; the `for` source is the model-filtered match list, so the menu narrows as the user types.

```html
<stack width="240">
  <combobox placeholder="Search frameworks" text="{frameworkQuery}" on-input="framework_edited" on-submit="commit_framework_query" on-press="open_framework_menu" />
  <if test="{frameworkMenuOpen}">
    <dropdown-menu anchor="below" anchor-alignment="stretch" on-dismiss="close_framework_menu">
      <for each="matchingFrameworks" key="id" as="f">
        <menu-item on-press="pick_framework:{f.id}">{f.name}</menu-item>
      </for>
    </dropdown-menu>
  </if>
</stack>
```

In the primary TypeScript core, the submit arm commits the current model-owned query. The open and submit messages remain separate, so Enter can commit without toggling the picker:

```ts
export type Msg =
  | { readonly kind: "framework_edited"; readonly edit: TextInputEvent }
  | { readonly kind: "open_framework_menu" }
  | { readonly kind: "close_framework_menu" }
  | { readonly kind: "commit_framework_query" };

case "commit_framework_query":
  return { ...model, committedFrameworkQuery: model.frameworkQuery, frameworkMenuOpen: false };
```

## Programmatic construction (Zig)

In a Zig view, the `canvas.Ui` builder constructs the same tree programmatically. `on_input` takes a comptime message constructor: `Ui.inputMsg(.tag)` builds `Msg{ .tag = edit }` for each `canvas.TextInputEvent`.

```zig
ui.stack(.{ .width = 240 }, .{
    ui.el(.combobox, .{
        .placeholder = "Search frameworks",
        .text = model.framework_query,
        .on_input = Ui.inputMsg(.framework_edited),
        .on_submit = .commit_framework_query,
        .on_press = .open_framework_menu,
    }, .{}),
    if (model.framework_menu_open) ui.el(.dropdown_menu, .{
        .anchor = .below,
        .anchor_alignment = .stretch,
        .on_dismiss = .close_framework_menu,
    }, .{
        ui.el(.menu_item, .{ .text = "Native SDK", .on_press = .{ .pick_framework = 0 } }, .{}),
        ui.el(.menu_item, .{ .text = "Canvas", .on_press = .{ .pick_framework = 1 } }, .{}),
    }) else ui.spacer(0),
})
```

## Attributes

| Attribute     | Description                                                                                                                                                                                                                                                                                                   |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text`        | Text value for text-bearing elements; a literal or one \{binding}.                                                                                                                                                                                                                                            |
| `placeholder` | Hint text shown while a text entry is empty.                                                                                                                                                                                                                                                                  |
| `disabled`    | Disables the control; true/false or a \{binding}.                                                                                                                                                                                                                                                             |
| `on-press`    | Dispatch a Msg on press: tag or tag:\{payload}. Legal on any element — a bound press handler makes it pressable, and presses on plain text/icons inside it fall through to it (dragging still selects text).                                                                                                  |
| `on-input`    | Names a Msg variant with canvas.TextInputEvent payload; delivers each text edit.                                                                                                                                                                                                                              |
| `on-submit`   | Dispatch a Msg on submit: tag or tag:\{payload}. Enter in a text field, primary+enter in a textarea; on a list-item, plain Enter dispatches it as the row's primary action while Space keeps the row's select (on-press).                                                                                     |
| `on-dismiss`  | Dismissible surfaces only (dialog, drawer, sheet, dropdown-menu): Msg dispatched when Escape or a click outside dismisses the surface, so the MODEL owns the close (clear the open flag in update). The engine hides the surface immediately as an optimistic echo; the source tree wins on the next rebuild. |

---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)