Designing the complex form

Part 02 · Field types

Selects, dropdowns, and the comboboxes in between

Native <select>, custom listboxes, multi-select, search-as-you-type. When each one is the right answer — and when it's the wrong one in disguise.

01

The decision tree

Most teams reach for a custom select before they ask whether they need one. The native one is unbeautiful on desktop, gets visually rejected in a comp review, and the team moves on to building. Months later the custom select has a keyboard navigation bug, a screen-reader bug, and a mobile picker that doesn't look anything like the OS picker the user expects.

I've lost more project weeks to teams replacing native selects than to almost any other single decision. The native one is doing a lot for you for free.

Before you build a custom select, name the specific thing native can't do. If you can't name one, native wins.

02

When native wins

Native <select> is the right answer more often than design systems want to admit. It's free-ARIA, free-keyboard, and the mobile picker is platform-quality — the iOS wheel and the Android modal are user-recognized patterns the OS has already perfected.

Where native is the default choice

  • Single-select with simple options — a few-to-many flat list of strings.
  • Anywhere the form will be filled more often on mobile than on desktop. The native picker on iOS and Android is better than anything you'll build in a sprint.
  • Anywhere the team doesn't have a dedicated accessibility budget. The minimum bar on custom is months of work to match what native gives away.

The visual complaint — "native looks ugly" — has a one-line fix. Restyle the trigger, leave the popup native.

select {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  padding: 0.75rem 2.5rem 0.75rem 1rem;
  background-image: url("data:image/svg+xml,...chevron...");
  background-repeat: no-repeat;
  background-position: right 1rem center;
}
Restyle the closed select; keep the native open popup

On click, you get the OS picker for free. Best of both.

03

When custom is justified

There are real reasons to build a custom select. They are narrower than most teams assume.

  • Option content that isn't a single string — image plus label plus secondary text, for instance.
  • Inline section headers and visual grouping that <optgroup> can't carry.
  • Type-to-search inside the list. (At that point it's a combobox, not a select — see part 05.)
  • Async-loaded options, where the list is fetched server-side as the user types.

Anything else is a styling preference dressed up as a functional requirement. If the design team says "the native picker doesn't match our system," the answer is to match the trigger, not rebuild the popup.

04

Multi-select — the field designers reach for too quickly

The multi-select pulldown — a dropdown that opens to a list of checkboxes — is one of the most-reached-for and most-wrong patterns in enterprise UI. It hides the selected state inside a closed dropdown, makes the count of selections invisible at a glance, and creates a click-target marathon to manage anything more than three picks.

Most multi-select fields are the wrong field type underneath. Two checks before you pick the control.

  1. 01If users pick from a short fixed list and the picks are visible most of the time, use inline checkboxes. No dropdown. The selections are always on screen.
  2. 02If users pick from a long list, frequently change their picks, or want to search for the option, use a combobox with selected items shown as chips outside the dropdown. The picks are visible; the searchable list is collapsed when not in use.

If you can't see what you've picked without opening the field, the field is the wrong shape.

The chip pattern needs three things to work: every chip is dismissable (X icon plus keyboard backspace from the input), the input remains usable when chips overflow (chips wrap, not horizontally scroll), and selections persist across the dropdown opening and closing.

05

Search-as-you-type (the combobox)

A combobox is a different ARIA role from a select. The user can type freely in the input, results filter as they type, and the user picks an option from the filtered list — or, if the pattern allows it, leaves their typed value as the answer.

Rules I won't compromise on

  • The input is instant. Every keystroke updates the rendered value. The fetch to the server is debounced — usually 200ms. These are different things; never debounce the input itself.
  • Highlight the matched substring in each result. The user's eye finds the match faster, and the relevance of fuzzy matches becomes legible.
  • Allow clearing the input with a visible X and with the Escape key. Both are expected.
  • Selection puts the option text in the input, closes the listbox, and fires change. The input doesn't go blank after selection; the user can edit from there.

06

Mobile picker quirks

Mobile select behavior is where the cost of custom shows up sharpest. Native <select> on iOS opens the wheel picker — full-screen, native momentum, OK/Cancel buttons. Native on Android opens a modal list. Both are platform conventions the user already knows.

Custom mobile pickers fail in predictable places.

  • Dropdown anchored to a small trigger doesn't fit options that wrap. Open as a bottom sheet or full-screen modal instead.
  • Tiny touch targets — option rows must be at least 44px tall, with generous horizontal padding.
  • Backdrop dismiss missing — tapping outside the picker has to close it. Without it, mobile users get trapped.
  • Keyboard suppression — when the picker opens, the soft keyboard from the input behind it should hide. Otherwise the picker is half-occluded.
  • Search inside the picker assumes a keyboard appears — sticky search-input at the top of the modal, keyboard auto-focused only if the list is long enough to need it.

The shortest version of this article — pick native first, restyle the trigger, build custom only when you've named the specific thing native can't do. Most of the time, you can't name one.