Skip to main content

Autocomplete

The Autocomplete component provides a flexible way to search and select from a list of options. It supports both single and multiple selections, custom filtering, and creating new options.

Basic Usage

import Autocomplete from '@myunisoft/design-system';

// Simple autocomplete with multiple selection
<Autocomplete
multiple
label="Select items"
options={[
{ id: 1, label: 'Option 1' },
{ id: 2, label: 'Option 2' },
{ id: 3, label: 'Option 3' }
]}
onChange={(selectedOptions) => {
console.log('Selected:', selectedOptions);
}}
/>

// Single selection autocomplete
<Autocomplete
label="Select a country"
options={countries}
disableClearable={true}
onChange={(selectedOption) => {
console.log('Selected:', selectedOption);
}}
/>

Pre-selected and Disabled Options

You can mark options as pre-selected or disabled:

<Autocomplete
multiple
label="Team members"
options={[
{
id: 1,
label: 'John Doe',
selected: true
},
{
id: 2,
label: 'Jane Smith',
selected: false
},
{
id: 3,
label: 'Admin User',
selected: true,
disabled: true
}
]}
onChange={(selectedList) => {
console.log('Selected team members:', selectedList);
}}
/>
France
Italie
France
Italie
France
Italie
France
Italie

Error State

Display validation errors by setting the isError prop:

<Autocomplete
multiple
label="Required field"
isError={true}
options={[
{
id: 1,
label: 'Selected item',
selected: true
},
{
id: 2,
label: 'Available item'
}
]}
onChange={(selected) => {
// Validate and update error state
if (selected.length === 0) {
setError(true);
}
}}
/>
Adrien
Tous

Implement custom filtering logic with the customSearch prop. This function receives the search text and the full options list, and should return a filtered array:

import { useState } from 'react';

const EMPLOYEES = [
{
id: 1,
label: 'Adam Smith',
department: 'Engineering',
email: 'adam@example.com'
},
{
id: 2,
label: 'Alex Johnson',
department: 'Sales'
},
{
id: 3,
label: 'Julian Martinez',
department: 'Engineering'
},
{
id: 4,
label: 'Mohamed Ali',
department: 'Marketing'
}
];

function EmployeeAutocomplete() {
const [selected, setSelected] = useState([]);

// Custom search that filters by names starting with 'A' or by department
const customSearch = (searchText, optionsList) => {
const query = searchText.toLowerCase();
return optionsList.filter((option) => {
const nameMatch = option.label?.toLowerCase().startsWith(query);
const deptMatch = option.department?.toLowerCase().includes(query);
return nameMatch || deptMatch;
});
};

return (
<Autocomplete
multiple
label="Search employees"
options={EMPLOYEES}
onChange={setSelected}
customSearch={customSearch}
/>
);
}

Hide Label

You can hide the label by setting isDisplayLabel to false. This is useful when the autocomplete is embedded in a context where the label is redundant:

<Autocomplete
multiple
label="Hidden label"
isDisplayLabel={false}
options={[
{
id: 1,
label: 'Selected option',
selected: true
},
{
id: 2,
label: 'Another option'
}
]}
onChange={(selected) => {
console.log('Selection changed:', selected);
}}
/>
Adrien
Tous

Track Dropdown State

Use the onIsSearchingChange callback to monitor when the dropdown is opened or closed. This is useful for triggering side effects like analytics or loading data:

import { useState } from 'react';

function TrackedAutocomplete() {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);

return (
<Autocomplete
multiple
label="Monitored autocomplete"
onIsSearchingChange={(isSearching) => {
setIsDropdownOpen(isSearching);
console.log('Dropdown is now:', isSearching ? 'open' : 'closed');

// Example: Load additional data when dropdown opens
if (isSearching) {
// fetchAdditionalOptions();
}
}}
options={[
{
id: 1,
label: 'Option 1',
selected: true
},
{
id: 2,
label: 'Option 2'
}
]}
/>
);
}
Adrien
Tous

Custom Tag Rendering

The renderTag prop allows you to customize how selected options are displayed as chips in multiple selection mode. This is useful when you need custom styling, icons, or behavior for the tags:

import { Chip } from '@mui/material';
import StarIcon from '@mui/icons-material/Star';

<Autocomplete
multiple
label="Select favorites"
options={[
{ id: 1, label: 'React', optionType: 'primary' },
{ id: 2, label: 'Vue', optionType: 'success' },
{ id: 3, label: 'Angular', optionType: 'warning' }
]}
renderTag={(chipProps, option) => (
<Chip
{...chipProps}
icon={<StarIcon />}
color={option.optionType === 'primary' ? 'primary' : 'default'}
variant="outlined"
/>
)}
onChange={(selected) => {
console.log('Selected:', selected);
}}
/>;

The renderTag function receives two parameters:

  • chipProps: ChipProps object containing the default chip properties (label, onDelete, etc.)
  • option: The OptionType object for the selected item
High Priority
Medium Priority
High Priority
Medium Priority

Custom Option Label Rendering

The renderOptionLabel prop allows you to customize how option labels are displayed in the dropdown menu. This is particularly useful for adding badges, icons, or additional information to options:

import { Box, Badge } from '@mui/material';
import WarningMuiIcon from '@mui/icons-material/Warning';

<Autocomplete
multiple
label="Select team members"
options={[
{ id: 1, label: 'Alice Johnson', role: 'Admin', verified: true },
{ id: 2, label: 'Bob Smith', role: 'Developer', verified: false, disabled: true },
{ id: 3, label: 'Charlie Brown', role: 'Designer', verified: true }
]}
renderOptionLabel={(labelChild, option) => (
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ fontWeight: 500 }}>{labelChild}</Box>
<Box sx={{ fontSize: '0.75rem', color: 'text.secondary' }}>
{option.role}
</Box>
</Box>
{!option.verified && (
<WarningMuiIcon color="error" />
)}
</Box>
)}
/>;

The renderOptionLabel function receives two parameters:

  • labelChild: ReactNode containing the default label with search highlighting
  • option: The OptionType object for the option being rendered

Custom Option Comparison

The isOptionEqualToValue prop allows you to define custom logic for comparing options. This is essential when working with options that have duplicate labels or when you need to compare by a specific property other than reference equality:

const usersWithDuplicateNames = [
{ id: 'user-1', label: 'John Smith', email: 'john1@company.com' },
{ id: 'user-2', label: 'John Smith', email: 'john2@company.com' },
{ id: 'user-3', label: 'Jane Doe', email: 'jane@company.com' }
];

<Autocomplete
multiple
label="Select users"
options={usersWithDuplicateNames}
isOptionEqualToValue={(option, value) => option.id === value.id}
getOptionLabel={(option) => `${option.label} (${option.email})`}
onChange={(selected) => {
console.log('Selected users:', selected);
}}
/>

Without isOptionEqualToValue, the component would use reference equality, which could cause issues with duplicate labels or when recreating option objects. By comparing IDs, you ensure accurate selection tracking.

John Smith (john1@company.com)
John Smith (john1@company.com)

Custom Label Extraction

The getOptionLabel prop allows you to specify how to extract the display label from complex option objects. This is useful when your options have nested properties or when you need to format the label:

const products = [
{ id: 1, name: 'Laptop', category: 'Electronics', price: 999 },
{ id: 2, name: 'Desk Chair', category: 'Furniture', price: 299 },
{ id: 3, name: 'Monitor', category: 'Electronics', price: 449 }
];

<Autocomplete
multiple
label="Select products"
options={products}
getOptionLabel={(option) => `${option.name} - $${option.price}`}
onChange={(selected) => {
console.log('Selected:', selected);
}}
/>
Laptop - $999
Laptop - $999

Disable Automatic Sorting

By default, the Autocomplete component sorts options alphabetically by their labels. Use the disableSort prop to preserve the original order of options:

const prioritizedOptions = [
{ id: 1, label: 'Most Important' },
{ id: 2, label: 'Second Priority' },
{ id: 3, label: 'Third Priority' },
{ id: 4, label: 'Least Important' }
];

<Autocomplete
multiple
label="Select by priority"
options={prioritizedOptions}
disableSort={true}
onChange={(selected) => {
console.log('Selected:', selected);
}}
/>

This is particularly useful when:

  • Options are manually ordered by priority or relevance
  • You want to maintain a specific business logic order
  • Options are grouped and should stay in their original grouping

Default behavior (alphabetically sorted):

With disableSort (original order preserved):

Hide Select All

Use the selectAllHidden prop to hide the "Select All" option in multiple selection mode. This is useful when you want users to manually select individual options:

<Autocomplete
multiple
label="Select team members"
selectAllHidden={true}
options={[
{ id: 1, label: 'Alice' },
{ id: 2, label: 'Bob' },
{ id: 3, label: 'Charlie' }
]}
onChange={(selected) => {
console.log('Selected:', selected);
}}
/>

Custom Select All Labels

You can customize the labels for "Select All" functionality using two separate props:

  • selectAllLabel: Customizes the label shown in the dropdown menu for the "Select All" option
  • allSelectedLabel: Customizes the label shown in the chip when all options are selected

This separation is useful for internationalization or when you want different terminology for these two contexts:

<Autocomplete
multiple
label="Select team members"
selectAllLabel="Select Everyone"
allSelectedLabel="All Members"
options={[
{ id: 1, label: 'Alice' },
{ id: 2, label: 'Bob' },
{ id: 3, label: 'Charlie' }
]}
onChange={(selected) => {
console.log('Selected:', selected);
}}
/>

In this example:

  • The dropdown will show "Select Everyone (3)" as the select all option
  • When all items are selected, the chip will display "All Members"

Default labels ("Tous"):

Custom dropdown label only (selectAllLabel):

Different labels for dropdown and chip:

Input Variants

The Autocomplete component supports three Material-UI TextField variants: standard (default), outlined, and filled. Choose the variant that best matches your UI design:

// Standard variant (default)
<Autocomplete
multiple
label="Standard variant"
options={countries}
/>

// Outlined variant
<Autocomplete
multiple
label="Outlined variant"
variant="outlined"
options={countries}
/>

// Filled variant
<Autocomplete
multiple
label="Filled variant"
variant="filled"
options={countries}
/>

Disable "All" Selected Tag

By default, when all options are selected in multiple mode, the Autocomplete displays a single "All" chip instead of individual chips. You can disable this behavior with disableAllSelectedTag:

<Autocomplete
multiple
label="Select team members"
disableAllSelectedTag
options={[
{ id: 1, label: 'John Doe', selected: true },
{ id: 2, label: 'Jane Smith', selected: true },
{ id: 3, label: 'Bob Johnson', selected: true }
]}
onChange={(selected) => {
console.log('Selected:', selected);
}}
/>

With disableAllSelectedTag, even when all options are selected, individual chips will be displayed for each option instead of a single "All" chip.

Default behavior (shows "All" chip):

France
Germany
Spain
Tous

With disableAllSelectedTag (shows individual chips):

France
Germany
Spain
France
Germany
Spain

Performance with Large Datasets

The Autocomplete component is optimized to handle large datasets efficiently. Here's an example with 10,000 randomly generated options:

import { useMemo } from 'react';

function LargeDatasetAutocomplete() {
// Generate 10,000 random options
const largeOptionsList = useMemo(() => {
return Array.from({ length: 10000 }, (_, index) => ({
id: index + 1,
label: `Option ${index + 1} - ${Math.random().toString(36).substring(2, 15)}`
}));
}, []);

return (
<Autocomplete
multiple
disableSort
label="Select from 10,000 options"
options={largeOptionsList}
onChange={(selected) => {
console.log(`Selected ${selected.length} items`);
}}
/>
);
}

The component uses virtualization internally to ensure smooth scrolling and quick search performance even with thousands of options. You can type to filter the list or use "Select All" functionality without performance degradation.

API Reference

Component Signature

function Autocomplete<T extends OptionType = OptionType>(props: AutocompleteProps<T>): React.ReactElement

The Autocomplete component is generic and accepts a type parameter T that extends OptionType. This allows you to use custom option types with additional properties while maintaining type safety.

  • Type Parameter: T extends OptionType = OptionType - Your custom option type (defaults to OptionType)
  • Props: AutocompleteProps<T> - Component props typed with your custom option type
  • Returns: React.ReactElement

Component Props

PropTypeRequiredDefaultDescription
multiplebooleanNofalseEnable multiple selection mode. When true, users can select multiple options.
optionsT[]Yes[]Array of option objects. See Option interface below for structure. Type-safe with your custom option type.
valueT | T[] | nullNoundefinedControlled value of the autocomplete. Use T for single mode, T[] for multiple mode. When undefined, component operates in uncontrolled mode.
onChange(newValue: T | T[] | null) => voidNoundefinedCallback fired when selection changes. Receives the new value (single option, array of options, or null). Type matches the value prop type.
labelstringNo'Selectionnez'Label displayed above the input field.
isDisplayLabelbooleanNotrueControls label visibility. Set to false to hide the label.
isDisabledbooleanNofalseDisables the entire component when true.
isErrorbooleanNofalseApplies error styling to indicate validation failure.
customSearch(searchText: string, options: T[]) => T[]NoundefinedCustom filter function. Receives search text and options array, returns filtered options. Type-safe with your custom option type.
onIsSearchingChange(isOpen: boolean) => voidNo() => {}Callback fired when dropdown opens/closes. Receives boolean indicating dropdown state.
isOptionsMutablebooleanNofalseWhen true, allows direct mutation of the options array passed as prop.
oneRowModebooleanNofalseLimits the autocomplete input to a single line when true.
disableClearablebooleanNofalseWhen true, removes the clear button from single-select mode.
onCreate(label: string, options: T[]) => voidNoundefinedEnables "create new option" functionality. Called when user creates a new option with the entered label and current options array. Type-safe with your custom option type.
InputPropsStandardInputPropsNo{}Props passed directly to the underlying Material-UI Input component.
renderTag(tagProps: ChipProps, option: T) => ReactNodeNoundefinedCustom render function for selected option chips in multiple mode. Receives chip properties and the option object. Return a custom chip component or null to use default rendering. Type-safe with your custom option type.
renderOptionLabel(labelChild: ReactNode, option: T) => ReactNodeNoundefinedCustom render function for option labels in the dropdown. Receives the default label (with search highlighting) and the option object. Return a custom label component or null to use default rendering. Type-safe with your custom option type.
disableAllSelectedTagtrueNoundefinedWhen set to true, disables the "All" chip that normally appears when all options are selected in multiple mode. Individual chips for each selected option will be shown instead.
isOptionEqualToValue(option: T, value: T) => booleanNo(option, value) => option.id === value.idFunction to determine if an option is equal to a value. Essential for proper selection tracking, especially with duplicate labels or when comparing by specific properties. When using custom types, ensures type-safe comparison.
getOptionLabel(option: T) => stringNo(option) => option?.labelFunction to extract the display label from an option object. Useful for complex option objects or custom label formatting. When using custom types, provides type-safe access to all properties.
disableSorttrueNoundefinedWhen set to true, disables automatic alphabetical sorting of options. Options will be displayed in their original order.
selectAllHiddenbooleanNofalseWhen set to true, hides the "Select All" option in multiple selection mode.
selectAllLabelstringNoI18n.t('common.all')Customizes the label displayed for the "Select All" option in the dropdown menu in multiple selection mode. The label appears with the count (e.g., "Select All (5)").
allSelectedLabelstringNoI18n.t('common.all')Customizes the label displayed in the chip when all options are selected in multiple selection mode. This allows using different terminology for the chip than the dropdown option.
variant'standard' | 'outlined' | 'filled'No'standard'Material-UI TextField variant to use for the input field styling.

Option Interface

Each option in the options array should follow this structure:

PropertyTypeRequiredDefaultDescription
idstring | numberYes-Unique identifier for the option.
labelstringYes-Display text for the option.
selectedbooleanNofalse⚠️ Deprecated: Use the value prop on the Autocomplete component instead. When true, option is pre-selected on mount in uncontrolled mode.
disabledbooleanNofalseWhen true, option cannot be selected or deselected.
optionType'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning'No'default'Visual style variant for the option.

Controlled vs Uncontrolled

The Autocomplete component can be used in both controlled and uncontrolled modes:

Controlled mode (recommended): Pass the value and onChange props to manage the selection state externally.

const [selectedOptions, setSelectedOptions] = useState([]);

<Autocomplete
multiple
value={selectedOptions}
onChange={(newValue) => setSelectedOptions(newValue)}
options={options}
/>

Uncontrolled mode: Use the selected property on options for initial values. The component manages its own state internally.

<Autocomplete
multiple
options={[
{ id: 1, label: 'Option 1', selected: true },
{ id: 2, label: 'Option 2' }
]}
/>

Generic Type Support

The Autocomplete component is generic and can work with custom option types that extend the base OptionType. This is useful when you need to include additional properties in your options beyond the standard id, label, selected, and disabled fields.

Using Custom Option Types

To use a custom option type, define an interface that extends OptionType and pass it as a type parameter to the Autocomplete component:

import { useState } from 'react';
import Autocomplete, { OptionType } from '@myunisoft/design-system';

// Define your custom option type
interface TeamMember extends OptionType {
email: string;
role: string;
department: string;
}

function TeamSelector() {
const [selected, setSelected] = useState<TeamMember[]>([]);

const teamMembers: TeamMember[] = [
{
id: 1,
label: 'John Doe',
email: 'john@company.com',
role: 'Senior Developer',
department: 'Engineering'
},
{
id: 2,
label: 'Jane Smith',
email: 'jane@company.com',
role: 'Product Designer',
department: 'Design'
},
{
id: 3,
label: 'Bob Johnson',
email: 'bob@company.com',
role: 'Engineering Manager',
department: 'Engineering'
}
];

return (
<Autocomplete<TeamMember>
multiple
label="Select Team Members"
options={teamMembers}
value={selected}
onChange={(newValue) => setSelected(newValue)}
getOptionLabel={(option) => `${option.label} - ${option.role}`}
isOptionEqualToValue={(option, value) => option.id === value.id}
/>
);
}

Benefits of Generic Types

  • Type Safety: Get full TypeScript type checking for your custom properties
  • IntelliSense: Auto-completion for custom properties in callbacks like getOptionLabel, renderTag, and renderOptionLabel
  • Flexibility: Add any properties needed for your use case while maintaining the component's core functionality
  • Reusability: Create specialized autocomplete components for different data types

Common Use Cases

Product Selection:

interface Product extends OptionType {
price: number;
category: string;
inStock: boolean;
}

<Autocomplete<Product>
label="Select Product"
options={products}
getOptionLabel={(option) => `${option.label} - $${option.price}`}
onChange={(selected) => {
// selected has full Product type with all properties
console.log(selected?.price, selected?.category);
}}
/>

User with Complex Data:

interface User extends OptionType {
avatar: string;
lastActive: Date;
permissions: string[];
}

<Autocomplete<User>
multiple
label="Select Users"
options={users}
renderOptionLabel={(labelChild, option) => (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Avatar src={option.avatar} sx={{ mr: 1 }} />
{labelChild}
</Box>
)}
/>

Note: When not specifying a type parameter, the component defaults to using the standard OptionType, maintaining backward compatibility with existing code.