Azalea

SearchField

A search box that is a GET form.

Why it exists

Six of these existed — Mint's toolbar, Mint's search page, Mint's search box, Harvest's list chrome, Harvest's search page, Harvest's forms — and every one had independently discovered the same non-obvious requirement, in four different ways.

A bare GET form replaces the whole query string

So typing a name silently drops the status filter the user had chosen. It is maddening, and it reads as the search being broken rather than the filter being discarded. Harvest wrote the fix out longhand; Mint's toolbar wrote a different version of it; Mint's search page did not have the problem because it had no other filters — which is the version most likely to grow the bug later.

The other filters ride along

Everything in params except this field and page is re-submitted as a hidden input. Switch to the code tab: the form below is carrying a status and a team it never displays.

And page is dropped, not carried

A new search on page 4 of the old results is a page that does not exist yet. The pager then reads “No records on page 4” for a query with plenty of matches — a failure that looks like the search found nothing.

In both themes

light

dark

Guidance

Use it when

  • Any filtered list. Pass the page's searchParams straight in.
  • A placeholder that names what is searchable — “A name, a company, an email address” beats “Search…”.
  • drop for anything else a new query invalidates: a cursor, a selection, a sub-tab.

Reach for something else when

  • An onChange that pushes to the router. It fires per keystroke, fills the history stack, and re-runs the query on every letter.
  • A bare <input>. It loses the ring, the themed border and the disabled styling, and none of that is visible in review — tests/accessibility.test.ts asserts this one composes Azalea's Input.
  • Searching client-side over a paged list. It searches the page, not the list, and looks like the data is missing.

Props

PropTypeDefaultDescription
paramsRecord<string, string | string[]>The current query string — Next's searchParams. Re-submitted as hidden inputs.
namestring"q"The parameter this field owns.
dropstring[]Reset by a new search. page is always dropped.
placeholderstringRequired. Also the accessible name unless label is given.
actionstring""Where the form posts. Defaults to the current URL.
childrenReactNodeRendered inside the form after the input — a submit button, an extra filter.

Accessibility

role="search" on the form makes it a landmark, so it is reachable directly rather than by tabbing to it. type="search" gets the platform's own clear button and, on iOS, a keyboard whose return key says “Search”. There is an sr-only submit button: Enter submits natively and needs no button, but touch has no Enter key, and a form whose only way to submit is a key that does not exist is a form nobody on a phone can use.