The Suggest component provides text input with autocomplete/typeahead suggestions, allowing users to type and select from filtered results.
React component for text input with autocomplete suggestions.
/**
* Suggest component for text input with autocomplete functionality
* @template T - Type of items in the suggestion list
*/
class Suggest<T> extends React.Component<SuggestProps<T>, SuggestState<T>> {
/** Generic factory method for type inference */
static ofType<U>(): new (props: SuggestProps<U>) => Suggest<U>;
/** Reference to the input element */
inputElement: HTMLInputElement | null;
}
interface SuggestProps<T> extends ListItemsProps<T>, Omit<SelectPopoverProps, "popoverTargetProps"> {
/** Function to convert selected item to display string in input */
inputValueRenderer: (item: T) => string;
/** Currently selected item */
selectedItem?: T | null;
/** Default selected item for uncontrolled usage */
defaultSelectedItem?: T;
/** Whether to close suggestions on item selection */
closeOnSelect?: boolean;
/** Whether the suggest is disabled */
disabled?: boolean;
/** Whether the suggest should fill its container */
fill?: boolean;
/** Props for the input element */
inputProps?: Partial<Omit<InputGroupProps, "disabled" | "fill" | "value" | "onChange">>;
/** Props for the dropdown menu container */
menuProps?: React.HTMLAttributes<HTMLUListElement>;
/** Whether to open suggestions on keydown */
openOnKeyDown?: boolean;
/** Whether to reset input value when suggestions close */
resetOnClose?: boolean;
}
interface SuggestState<T> {
/** Whether the suggestions dropdown is open */
isOpen: boolean;
/** Currently selected item */
selectedItem: T | null;
}Usage Examples:
import React, { useState } from "react";
import { Suggest, ItemRenderer } from "@blueprintjs/select";
import { MenuItem } from "@blueprintjs/core";
interface Country {
name: string;
code: string;
continent: string;
}
const countries: Country[] = [
{ name: "United States", code: "US", continent: "North America" },
{ name: "Canada", code: "CA", continent: "North America" },
{ name: "United Kingdom", code: "GB", continent: "Europe" },
{ name: "Germany", code: "DE", continent: "Europe" },
{ name: "Japan", code: "JP", continent: "Asia" },
];
const renderCountry: ItemRenderer<Country> = (country, { handleClick, modifiers, query }) => (
<MenuItem
key={country.code}
text={country.name}
label={country.code}
onClick={handleClick}
active={modifiers.active}
disabled={modifiers.disabled}
/>
);
const CountrySuggest = () => {
const [selectedCountry, setSelectedCountry] = useState<Country | null>(null);
return (
<Suggest<Country>
items={countries}
itemRenderer={renderCountry}
inputValueRenderer={(country) => country.name}
selectedItem={selectedCountry}
onItemSelect={setSelectedCountry}
noResults={<MenuItem disabled text="No countries found." />}
inputProps={{ placeholder: "Type to search countries..." }}
closeOnSelect
/>
);
};
// Uncontrolled usage with default value
const UncontrolledCountrySuggest = () => (
<Suggest<Country>
items={countries}
itemRenderer={renderCountry}
inputValueRenderer={(country) => country.name}
defaultSelectedItem={countries[0]}
onItemSelect={(country) => console.log("Selected:", country.name)}
inputProps={{ placeholder: "Search countries..." }}
/>
);The inputValueRenderer prop is required and determines how selected items are displayed in the input field.
type InputValueRenderer<T> = (item: T) => string;Usage Examples:
interface User {
firstName: string;
lastName: string;
email: string;
id: number;
}
const users: User[] = [
{ firstName: "John", lastName: "Doe", email: "john@example.com", id: 1 },
{ firstName: "Jane", lastName: "Smith", email: "jane@example.com", id: 2 },
];
// Display full name in input
const UserSuggestByName = () => (
<Suggest<User>
items={users}
itemRenderer={(user, { handleClick, modifiers }) => (
<MenuItem
text={`${user.firstName} ${user.lastName}`}
label={user.email}
onClick={handleClick}
active={modifiers.active}
/>
)}
inputValueRenderer={(user) => `${user.firstName} ${user.lastName}`}
onItemSelect={(user) => console.log("Selected user:", user)}
inputProps={{ placeholder: "Search by name..." }}
/>
);
// Display email in input
const UserSuggestByEmail = () => (
<Suggest<User>
items={users}
itemRenderer={(user, { handleClick, modifiers }) => (
<MenuItem
text={user.email}
label={`${user.firstName} ${user.lastName}`}
onClick={handleClick}
active={modifiers.active}
/>
)}
inputValueRenderer={(user) => user.email}
onItemSelect={(user) => console.log("Selected user:", user)}
inputProps={{ placeholder: "Search by email..." }}
/>
);Suggest supports custom filtering logic for sophisticated search behaviors.
// Inherited from ListItemsProps
type ItemPredicate<T> = (query: string, item: T, index?: number, exactMatch?: boolean) => boolean;
type ItemListPredicate<T> = (query: string, items: T[]) => T[];Usage Examples:
// Multi-field search predicate
const userPredicate: ItemPredicate<User> = (query, user) => {
const searchTerms = query.toLowerCase().split(" ");
const searchableText = [
user.firstName.toLowerCase(),
user.lastName.toLowerCase(),
user.email.toLowerCase(),
].join(" ");
return searchTerms.every(term => searchableText.includes(term));
};
// Custom list predicate with scoring
const scoredUserPredicate: ItemListPredicate<User> = (query, users) => {
if (!query) return users;
const scored = users.map(user => {
let score = 0;
const q = query.toLowerCase();
// Exact match bonus
if (user.firstName.toLowerCase() === q) score += 100;
if (user.lastName.toLowerCase() === q) score += 100;
// Starts with bonus
if (user.firstName.toLowerCase().startsWith(q)) score += 50;
if (user.lastName.toLowerCase().startsWith(q)) score += 50;
// Contains bonus
if (user.firstName.toLowerCase().includes(q)) score += 20;
if (user.lastName.toLowerCase().includes(q)) score += 20;
if (user.email.toLowerCase().includes(q)) score += 10;
return { user, score };
})
.filter(({ score }) => score > 0)
.sort((a, b) => b.score - a.score)
.map(({ user }) => user);
return scored;
};
const AdvancedFilterSuggest = () => (
<Suggest<User>
items={users}
itemRenderer={(user, { handleClick, modifiers }) => (
<MenuItem
text={`${user.firstName} ${user.lastName}`}
label={user.email}
onClick={handleClick}
active={modifiers.active}
/>
)}
inputValueRenderer={(user) => `${user.firstName} ${user.lastName}`}
itemListPredicate={scoredUserPredicate}
onItemSelect={(user) => console.log("Selected:", user)}
inputProps={{ placeholder: "Advanced search..." }}
/>
);Suggest provides extensive input customization through Blueprint's InputGroup props.
// Subset of InputGroup props available to Suggest
interface InputGroupProps {
/** Whether input should autofocus */
autoFocus?: boolean;
/** Input placeholder text */
placeholder?: string;
/** Whether input is disabled */
disabled?: boolean;
/** Whether input should fill container */
fill?: boolean;
/** Large size variant */
large?: boolean;
/** Small size variant */
small?: boolean;
/** Left icon */
leftIcon?: IconName;
/** Right element (icon or component) */
rightElement?: React.ReactNode;
/** Input type */
type?: string;
/** Additional CSS classes */
className?: string;
/** Callback when input value changes */
onChange?: React.ChangeEventHandler<HTMLInputElement>;
/** Callback when input receives focus */
onFocus?: React.FocusEventHandler<HTMLInputElement>;
/** Callback when input loses focus */
onBlur?: React.FocusEventHandler<HTMLInputElement>;
}Usage Examples:
import { Suggest } from "@blueprintjs/select";
import { Button, InputGroup } from "@blueprintjs/core";
const CustomInputSuggest = () => {
const [value, setValue] = useState("");
return (
<Suggest<string>
items={["Apple", "Banana", "Cherry", "Date"]}
itemRenderer={(item, { handleClick, modifiers }) => (
<div onClick={handleClick} style={{ padding: "8px" }}>
{item}
</div>
)}
inputValueRenderer={(item) => item}
onItemSelect={(item) => setValue(item)}
inputProps={{
leftIcon: "search",
placeholder: "Search fruits...",
large: true,
fill: true,
rightElement: value ? (
<Button
icon="cross"
minimal
small
onClick={() => setValue("")}
/>
) : undefined,
value,
onChange: (e) => setValue(e.target.value),
}}
/>
);
};Suggest can be used in both controlled and uncontrolled modes.
Usage Examples:
// Controlled usage
const ControlledSuggest = () => {
const [selectedCountry, setSelectedCountry] = useState<Country | null>(null);
const [inputValue, setInputValue] = useState("");
return (
<Suggest<Country>
items={countries}
itemRenderer={renderCountry}
inputValueRenderer={(country) => country.name}
selectedItem={selectedCountry}
onItemSelect={(country) => {
setSelectedCountry(country);
setInputValue(country.name);
}}
query={inputValue}
onQueryChange={(query) => setInputValue(query)}
inputProps={{ placeholder: "Controlled suggest..." }}
/>
);
};
// Uncontrolled usage
const UncontrolledSuggest = () => (
<Suggest<Country>
items={countries}
itemRenderer={renderCountry}
inputValueRenderer={(country) => country.name}
defaultSelectedItem={countries[0]}
onItemSelect={(country) => console.log("Selected:", country.name)}
inputProps={{ placeholder: "Uncontrolled suggest..." }}
/>
);Suggest supports comprehensive keyboard navigation and control.
Usage Examples:
const KeyboardSuggest = () => (
<Suggest<Country>
items={countries}
itemRenderer={renderCountry}
inputValueRenderer={(country) => country.name}
onItemSelect={(country) => console.log("Selected:", country.name)}
openOnKeyDown // Open suggestions when user starts typing
closeOnSelect // Close suggestions after selection
resetOnClose // Reset input value when suggestions close
inputProps={{
placeholder: "Type to search, use arrows to navigate, Enter to select...",
onKeyDown: (e) => {
// Custom keyboard handling
if (e.key === "Escape") {
e.currentTarget.blur();
}
},
}}
/>
);