figma guide
Designing combobox and autocomplete UI in Figma: patterns, states, and handoff
Design comboboxes, typeahead search, and multi-select autocomplete in Figma with list states, empty results, async loading, and Dev Mode specs for forms and filters.
- Published
- Updated
- Jun 14, 2026
- Read time
- 8 min
- Level
- Beginner
Quick answer
A combobox combines a text input with a filterable suggestion list—unlike a static dropdown, users type to narrow options. Build a Combobox component with variants for mode=single | multi, state=default | open | loading | empty | error, and size=sm | md. Pair with forms, search UI, and tables. Document debounce delay, min characters to search, async vs local data, create-new option, and keyboard navigation in Dev Mode. Start from the Figma guides hub for input and list patterns.
Who this is for
- Product designers shipping assignee pickers, address lookup, tag fields, and command palettes.
- Design system teams distinguishing combobox from select and search bar—three patterns that often get merged incorrectly.
- Engineers implementing ARIA combobox patterns, virtualized lists, and API-backed suggestions.
Combobox vs related patterns
| Pattern | User types? | List behavior | Best for |
|---|---|---|---|
| Combobox (single) | Yes | Filters as you type; pick one | Country, user assignee, repo name |
| Combobox (multi) | Yes | Adds chips; remove per tag | Tags, recipients, skills |
| Select / dropdown | No (click only) | Static list | <15 fixed options |
| Search bar | Yes | Navigates results page or live filter | Global search, search UI |
| Command palette | Yes | Actions + pages; keyboard-first | Power users, ⌘K shortcuts |
| Autocomplete only | Yes | Inserts inline text; no pick list | Email addresses in some clients |
Verdict: use a combobox when the option set is too large to scan but users still pick from known values. Use a search bar when results are pages or records, not a closed option list.
Component anatomy
| Part | Purpose | Spec tip |
|---|---|---|
| Input field | Type query; shows selected value | Reuse form text field |
| Chevron / clear | Open list or reset | Clear visible when value exists |
| Listbox popover | Suggestions below input | Max height 240–320px; scroll |
| Option row | One selectable item | 36–40px row height |
| Highlight | Match substring in bold | ”California” |
| Group label | Section header in list | ”Recent”, “All users” |
| Checkbox (multi) | Selected state per row | Leading or trailing—pick one |
| Chip tray (multi) | Selected values inside input | Wrap with auto layout |
| Loading row | Spinner + “Searching…” | Min 1 row height |
| Empty state | No matches | Link to empty state copy |
| Create option | ”Add …” row | When user can create new tag |
Combobox
├── Variant: mode=single | multi
├── Variant: state=default | open | loading | empty | error
├── Variant: size=sm | md
├── Layer: InputContainer
│ ├── Chips (multi only)
│ ├── Text input
│ ├── Clear button
│ └── Chevron
├── Layer: Listbox (popover)
│ ├── GroupLabel (optional)
│ ├── OptionRow × N
│ │ ├── Icon / avatar (optional)
│ │ ├── Primary label + highlight
│ │ ├── Secondary text (optional)
│ │ └── Checkmark / checkbox
│ ├── LoadingRow
│ ├── EmptyStateRow
│ └── CreateNewRow (optional)
└── Layer: Helper / error text
Use avatars in assignee pickers; use icons for country flags or file types. Bind list row hover to interactive components.
List and input states
| State | Input | Listbox | Notes |
|---|---|---|---|
| Closed | Shows selected label or placeholder | Hidden | Chevron down |
| Open / typing | Caret active; query visible | Filtered options | Opens on focus or after 1 char—document |
| Hover option | Unchanged | Row background highlight | Arrow keys move highlight |
| Selected | Field shows value; list closes | — | Single mode |
| Multi with chips | Chips + cursor for more typing | Stays open until blur | Backspace removes last chip |
| Loading | Input active | Spinner row | After debounce |
| Empty | Query with no matches | ”No results” + optional create | Do not leave blank popover |
| Error | Red border | May stay closed | ”Invalid selection” |
| Disabled | Muted | No open | Entire field inactive |
For async data, always design loading and empty frames—engineers will ship them even if you only mock the happy path.
Single-select combobox flow
| Step | UI | Spec |
|---|---|---|
| 1 | User focuses input | List opens with full or recent set |
| 2 | User types | List filters; highlight matching substring |
| 3 | Arrow down / up | Moves active option |
| 4 | Enter or click | Selects; list closes; input shows label |
| 5 | Esc | Closes list; restores previous value or clears query |
| 6 | Blur | Commit selection or revert—document |
Free text: if the user can submit a value not in the list (e.g., custom tag when allowCreate=true), show a Create ”…” row as the last option.
Multi-select and chips
| Concern | Design decision | Handoff note |
|---|---|---|
| Chip placement | Inside input (inline) vs below | Inline is standard for tags |
| Max visible chips | +N overflow chip | ”3 more” when >5 selected |
| Remove chip | X on chip; Backspace removes last | Both supported in most libs |
| Duplicate prevention | No duplicate chips | Case sensitivity rule |
| List stays open | Yes until blur | Faster multi-pick |
| Select all | Rare; checkbox in group header | Bulk assign only |
Align chip styles with badges and chips tokens—sm size inside inputs.
Async and performance patterns
| Pattern | When | UI to design |
|---|---|---|
| Local filter | <100 static options | Instant list filter |
| Debounced API | Large remote set | Loading row after 300ms |
| Min chars | Expensive queries | Helper: “Type 2+ characters” |
| Pagination | Long lists | ”Load more” row at bottom |
| Recent + all | Frequent picks | Group “Recent” above “All” |
| Virtualized list | 1000+ items | Same row design; note in spec |
Document debounceMs (e.g., 300), minQueryLength (2), and maxResults (20) in the component description.
Pairing with other patterns
| Need | Pattern | Link |
|---|---|---|
| Form field | Label + combobox + helper | Forms guide |
| Filter bar | Combobox for category / owner | Search UI |
| Table cell edit | sm combobox in cell | Tables |
| Modal create flow | Combobox for parent folder | Modals |
| Inline validation | Error + alert | Required assignee |
| No results | Empty state illustration in panel | First-time tag creation |
Accessibility checklist
| Requirement | Implementation |
|---|---|
| Role | role="combobox" on input; role="listbox" on list |
| Expanded | aria-expanded="true" when open |
| Active option | aria-activedescendant points to highlighted row |
| Label | Visible <label> or aria-label |
| Keyboard | Up/Down navigate; Enter selects; Esc closes |
| Multi | Announce selection count in live region |
| Loading | aria-busy="true" on listbox |
| Group labels | role="group" + aria-label |
Run accessibility plugins on list row contrast and focus visibility.
Prototyping limits
Figma cannot filter a real list. For demos:
- Build frames: closed, open with results, open empty, loading, multi with chips, error.
- Show highlighted substring as bold text in option rows (static examples).
- Use sticky notes for debounce, API, and keyboard behavior.
Wire input focus → open list frame if you need a clickable demo; organize with sections.
Handoff to engineering
| Deliverable | Where it lives |
|---|---|
mode | single vs multi |
| Data source | local array vs API endpoint |
| debounceMs | e.g., 300 |
| minQueryLength | e.g., 2 |
| value / label fields | id vs display string |
| allowCreate | boolean + create row copy |
| clearable | Show clear button when |
| closeOnSelect | false for multi |
| maxSelections | number or unlimited |
| Option template | icon, title, subtitle slots |
| Empty / loading copy | Exact strings |
| Popover placement | Flip above if needed |
Include comboboxes in your Dev Mode handoff checklist. Publish under Patterns / Forms in your team library.
Real-world examples
Assignee picker (issue tracker)
Multi combobox with avatars, type to filter teammates, recent group at top. Chips inside input; max 5 assignees with error toast if exceeded.
Address lookup
Single combobox, async loading after 3 characters, debounced 400ms. Secondary line shows city/state. Empty state: “No addresses found—check spelling.”
Skills tag field
Multi combobox with allowCreate=true. Create row: Add "GraphQL". Chips use neutral style; duplicate skills blocked.
Common mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Combobox vs select confusion | Wrong component shipped | Flowchart in design system doc |
| No loading state | Blank popover flash | Always design loading row |
| List taller than viewport | Clipped options | Max-height + scroll |
| Highlight only color | Match text unclear | Bold matched substring |
| Multi closes on each pick | Slow tagging | closeOnSelect=false |
| Tiny option rows (<36px) | Miss taps | 40px minimum |
| Async with no min chars | API spam | minQueryLength + helper text |
| Free text without create row | Users think pick failed | Explicit “Add …” option |
| Same UI as global search | Wrong mental model | Separate components |
FAQ
Combobox or dropdown?
Dropdown when users pick from a short fixed list without typing. Combobox when the list is long or remote and filtering helps.
Show all options before typing?
Recent + browse all is common. For huge sets, require min characters before showing API results.
Icons in every row?
Use when options are visually distinct (users, countries, file types). Skip for plain text enums.
Combobox inside a modal?
Yes—ensure listbox z-index above modal footer and flip placement if near bottom edge.
Bottom line
Design comboboxes as input + listbox systems: typing, filtering, loading, empty, selection, and chip behavior—not a select with a search icon pasted on. Document async rules, multi-select, and create-new flows in Dev Mode. Continue with forms, dropdowns, search UI, and the tutorials hub.
§ Keep reading