Anatomy
┌───────────────────────────────────────────────┐
│ 🔍 riverside │
├───────────────────────────────────────────────┤
│ CUSTOMERS (1) │
│ Riverside Fit-Out Ltd Leeds · Dana │
│ INVOICES (2) │
│ INV-1041 $8,400 overdue │
│ INV-1039 $2,000 paid │
│ JOBS (1) │
│ Phase 2 — Framing in progress │
│ ─────────────────────────── see all jobs → │
└───────────────────────────────────────────────┘
- Grouped by type, with counts. An undifferentiated list makes people read every row to work out what kind of thing it is. The heading does that work.
- Disambiguating detail on every row. Two customers called Marlow are one row each and identical without it.
- A door into the full list. Global search shows the strongest few; when the answer is not there, "see all" hands over to the section that can filter properly.
Why it works
It removes a decision the person should never have had to make: which part of the app owns this? That decision is an artefact of how the data is modelled, and asking a user to make it is asking them to hold your schema in their head.
It is also the honest fallback for imperfect navigation. Ranking a nav bar is a compromise; search is the escape valve for everything the compromise pushed down.
Where the quality actually comes from
Almost all of it is in ranking and matching, not layout:
- Identifiers must match exactly and rank first. People paste invoice numbers and emails. A fuzzy match that buries the exact hit is worse than no search.
- Prefix beats substring. "Mar" should surface Marlow before Kestrel Marine.
- Recency is a legitimate tie-breaker, because what someone touched this morning is usually what they mean.
- Search what people say, not what you store. If the business says "job" and
the table is
work_orders, both must match.
Getting it wrong
- One flat list, so the type of each result is a guess.
- Names only, so pasting an invoice number returns nothing — the single most common real search.
- A results page with no route onward when the match is not in the top five.
- Searching only the current section while looking global, which teaches people the box is unreliable and they stop using it.
Exemplars
Stripe's search takes an amount, an email, a card fingerprint or an object id and routes each to the right kind of record. It demonstrates that global search is mostly a matching problem: the interface is trivial, the ranking is the product.
Slack shows the grouping principle under pressure — messages, files, channels and people are different enough that one ranked list would be unusable.
The extractable rule: global search is a promise that the user does not need to know your data model. Every result type you leave out is a place that promise breaks, and it only has to break once.