SelectPro is the premium combobox/multi-select for Aggarly Design System: search, chips, keyboard support, optional virtualization for large lists, and API-backed infinite scroll through ApiSelect.
Use for large enumerations where search is faster than scrolling.
import { SelectPro } from "@/design-system/components/SelectPro";
export function Example() {
return (
<SelectPro
label="Environment"
placeholder="Select environment"
options={[
{ value: "prod", label: "Production", keywords: ["live"] },
{ value: "staging", label: "Staging" },
{ value: "sandbox", label: "Sandbox" },
]}
defaultValue="prod"
helper="Search + keyboard support."
/>
);
}
Chips + listbox. Backspace removes last chip when the query is empty.
import { SelectPro } from "@/design-system/components/SelectPro";
export function Example() {
return (
<SelectPro
multiple
label="Roles"
placeholder="Select roles"
searchPlaceholder="Search roles…"
options={[
{ value: "owner", label: "Owner" },
{ value: "admin", label: "Admin" },
{ value: "editor", label: "Editor" },
{ value: "viewer", label: "Viewer" },
{ value: "restricted", label: "Restricted" },
]}
defaultValue={["admin", "viewer"]}
helper="Multi-select chips + search."
/>
);
}
Enterprise pattern: assignee/owner selects with avatar + name.
import { SelectPro } from "@/design-system/components/SelectPro";
import { Avatar } from "@/design-system/components/Avatar";
const PEOPLE = [
{ id: "t1", name: "Workspace admin One", initials: "T1", tone: "primary" },
{ id: "su", name: "Supervisor", initials: "SU", tone: "success" },
];
export function Example() {
return (
<SelectPro
label="Assignee"
placeholder="Select a person"
options={PEOPLE.map((p) => ({
value: p.id,
label: p.name,
keywords: [p.initials],
meta: p,
}))}
renderLeading={(opt) => {
const p = opt.meta as any;
return <Avatar size="xs" name={p.name} initials={p.initials} tone={p.tone} variant="soft" />;
}}
renderOption={(opt) => {
const p = opt.meta as any;
return (
<>
<Avatar size="xs" name={p.name} initials={p.initials} tone={p.tone} variant="soft" />
<span className="selectpro__option-text">{p.name}</span>
</>
);
}}
/>
);
}
Enterprise pattern: team selectors. Chips render avatar + name.
import { SelectPro } from "@/design-system/components/SelectPro";
import { Avatar } from "@/design-system/components/Avatar";
export function Example() {
return (
<SelectPro
multiple
label="Care team"
placeholder="Select people"
options={[
{ value: "t1", label: "Workspace admin One", meta: { name: "Workspace admin One", initials: "T1", tone: "primary" } },
{ value: "su", label: "Supervisor", meta: { name: "Supervisor", initials: "SU", tone: "success" } },
]}
renderChipLabel={(opt) => {
const p = opt.meta as any;
return (
<>
<Avatar size="xs" name={p.name} initials={p.initials} tone={p.tone} variant="soft" />
<span>{p.name}</span>
</>
);
}}
renderOption={(opt) => {
const p = opt.meta as any;
return (
<>
<Avatar size="xs" name={p.name} initials={p.initials} tone={p.tone} variant="soft" />
<span className="selectpro__option-text">{p.name}</span>
</>
);
}}
/>
);
}
Recommended for performance. Only renders visible rows.
import { SelectPro } from "@/design-system/components/SelectPro";
export function Example() {
const options = Array.from({ length: 5000 }, (_, i) => ({
value: `opt-${i + 1}`,
label: `Option ${i + 1}`,
keywords: [`#${i + 1}`],
}));
return (
<SelectPro
label="Virtualized (5k+)"
placeholder="Search 5,000 options"
options={options}
virtualization={{ threshold: 400 }}
/>
);
}
Cursor pagination + infinite scroll (recommended for massive datasets like users).
import { ApiSelect } from "@/design-system/components/ApiSelect";
export function Example() {
return (
<ApiSelect
label="Users (paged API)"
placeholder="Search users"
url="/api/demo/users-paged"
queryParam="q"
minQueryLength={1}
preloadOnOpen={false}
pagination={{ enabled: true, pageSize: 50 }}
/>
);
}