Search with suggestions
Type ahead and pick from matching results.
Built with
Find a colleague
Search 8 people across Northwind by name, team or role.
Pick a result to see their contact details.
search-with-suggestions.tsx
"use client";
import { useState } from "react";
import { LuMail, LuMapPin } from "react-icons/lu";
import { Avatar, Badge, Combobox, SelectOption } from "@nexera-ui/react";
// Sample data
interface Person {
id: string;
name: string;
initials: string;
role: string;
team: string;
email: string;
location: string;
status: "Active" | "On leave" | "Remote" | "Invited";
}
const PEOPLE: Person[] = [
{
id: "p1",
name: "Ayesha Khan",
initials: "AK",
role: "Senior product designer",
team: "Design",
email: "ayesha.khan@northwind.io",
location: "London",
status: "Active",
},
{
id: "p2",
name: "Sara Malik",
initials: "SM",
role: "Payroll specialist",
team: "People Ops",
email: "sara.malik@northwind.io",
location: "Lisbon",
status: "On leave",
},
{
id: "p3",
name: "Ahmed Raza",
initials: "AR",
role: "Engineering manager",
team: "Engineering",
email: "ahmed.raza@northwind.io",
location: "Lahore",
status: "Remote",
},
{
id: "p4",
name: "Mia Jensen",
initials: "MJ",
role: "Product manager",
team: "Product",
email: "mia.jensen@northwind.io",
location: "Copenhagen",
status: "Active",
},
{
id: "p5",
name: "Luis Ortega",
initials: "LO",
role: "Frontend engineer",
team: "Engineering",
email: "luis.ortega@northwind.io",
location: "Madrid",
status: "Active",
},
{
id: "p6",
name: "Yuki Tanaka",
initials: "YT",
role: "Recruiter",
team: "People Ops",
email: "yuki.tanaka@northwind.io",
location: "Tokyo",
status: "Active",
},
{
id: "p7",
name: "Chloe Martin",
initials: "CM",
role: "Data analyst",
team: "Finance",
email: "chloe.martin@northwind.io",
location: "Paris",
status: "Remote",
},
{
id: "p8",
name: "Omar Haddad",
initials: "OH",
role: "Support lead",
team: "Customer",
email: "omar.haddad@northwind.io",
location: "Dubai",
status: "Invited",
},
];
const STATUS_TONE = {
Active: "success",
"On leave": "leave",
Remote: "remote",
Invited: "info",
} as const;
/** Search with suggestions: type a name, team or role and pick a colleague from the matching results. */
export default function SearchWithSuggestions() {
const [selected, setSelected] = useState<Person | null>(null);
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<section
aria-labelledby="directory-title"
className="mx-auto grid max-w-xl gap-5 rounded-xl border border-default bg-surface p-5 shadow-sm @3xl:p-8"
>
<header className="grid gap-1">
<h2 id="directory-title" className="text-heading-h3 text-primary">
Find a colleague
</h2>
<p className="text-body-default text-secondary">
Search {PEOPLE.length} people across Northwind by name, team or role.
</p>
</header>
<Combobox
label="Search people"
placeholder="Try “payroll” or “Sara”"
defaultItems={PEOPLE}
onChange={(key) => {
setSelected(PEOPLE.find((p) => p.id === key) ?? null);
}}
listboxProps={{
emptyTitle: (text) => `No one matches “${text}”`,
emptyDescription: "Check the spelling, or search by team instead.",
}}
>
{(person: Person) => (
<SelectOption
id={person.id}
label={person.name}
description={`${person.role} · ${person.team}`}
textValue={`${person.name} ${person.role} ${person.team}`}
leading={<Avatar size="sm" initials={person.initials} isDecorative />}
/>
)}
</Combobox>
{selected ? (
<article
aria-label={selected.name}
className="flex flex-wrap items-start gap-4 rounded-lg border border-subtle bg-sunken p-4"
>
<Avatar size="xl" initials={selected.initials} isDecorative />
<div className="grid min-w-0 flex-1 gap-2">
<div className="flex flex-wrap items-center gap-2">
<h3 className="text-heading-h4 text-primary">{selected.name}</h3>
<Badge label={selected.status} tone={STATUS_TONE[selected.status]} />
</div>
<p className="text-body-default text-secondary">
{selected.role}, {selected.team}
</p>
<ul className="grid gap-1 text-body-small text-secondary">
<li className="flex items-center gap-2">
<LuMail aria-hidden className="shrink-0 text-muted" />
<a
href={`#mailto-${selected.id}`}
className="break-all text-brand-text underline underline-offset-4"
>
{selected.email}
</a>
</li>
<li className="flex items-center gap-2">
<LuMapPin aria-hidden className="shrink-0 text-muted" />
{selected.location}
</li>
</ul>
</div>
</article>
) : (
<p className="rounded-lg border border-dashed border-default p-4 text-center text-body-small text-muted">
Pick a result to see their contact details.
</p>
)}
</section>
</div>
);
}
More forms and data entry blocks
- Contact formName, email, topic and message with validation.
- Add a personCreate a record in a modal.
- Multi-step formLong forms split into steps you can go back to.
- Address and phoneCountry, address lookup and phone with dialling code.
- File uploadDrag files in and watch each one upload.
- Date range pickerChoose a start and end date on a calendar.
- Terms and consentScrollable terms with a required checkbox.
- Choose an optionPick one option from a short list in a dialog.
- Pick a colourBrand or label colour from swatches or a picker.