Skip to main content

Overview

The Autocomplete component combines a text input with a filterable dropdown menu, allowing users to quickly find and select from a large list of options. It’s ideal when you have many options and want to help users find what they need through search.

Import

import { Autocomplete } from '@naturacosmeticos/natds-react'

Basic Usage

import React, { useState } from 'react'
import { Autocomplete } from '@naturacosmeticos/natds-react'

function App() {
  const [value, setValue] = useState('')
  
  const options = [
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'orange', label: 'Orange' }
  ]

  const handleSelect = (option) => {
    setValue(option.label)
  }

  return (
    <Autocomplete
      name="fruit"
      label="Select a fruit"
      value={value}
      options={options}
      handleSelect={handleSelect}
      onChange={(e) => setValue(e.target.value)}
    />
  )
}

With Search Filter

import React, { useState } from 'react'
import { Autocomplete, OptionProps } from '@naturacosmeticos/natds-react'

function SearchableAutocomplete() {
  const [value, setValue] = useState('')
  const [filteredOptions, setFilteredOptions] = useState<OptionProps[]>([])

  const allOptions = [
    { value: 'ca', label: 'California' },
    { value: 'co', label: 'Colorado' },
    { value: 'ct', label: 'Connecticut' },
    { value: 'fl', label: 'Florida' },
    { value: 'ny', label: 'New York' },
    { value: 'tx', label: 'Texas' }
  ]

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const query = e.target.value
    setValue(query)

    // Filter options based on input
    const filtered = allOptions.filter(option =>
      option.label.toLowerCase().includes(query.toLowerCase())
    )

    setFilteredOptions(
      filtered.length > 0
        ? filtered
        : [{ value: 'false', label: 'No results found' }]
    )
  }

  const handleSelect = (option: OptionProps) => {
    if (option.value !== 'false') {
      setValue(option.label)
    }
  }

  return (
    <Autocomplete
      name="state"
      label="State"
      placeholder="Search for a state"
      value={value}
      options={filteredOptions}
      handleSelect={handleSelect}
      onChange={handleChange}
      notFound="No states match your search"
    />
  )
}

Sizes

<Autocomplete
  name="medium"
  label="Medium Size"
  size="medium"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
/>

<Autocomplete
  name="mediumx"
  label="Medium X Size (Default)"
  size="mediumX"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
/>
// Menu opens below (default)
<Autocomplete
  name="default-position"
  label="Opens Below"
  position="bottom"
  value={value}
  options={options}
  handleSelect={handleSelect}
/>

// Menu opens above
<Autocomplete
  name="top-position"
  label="Opens Above"
  position="top"
  value={value}
  options={options}
  handleSelect={handleSelect}
/>

States

With Helper Text

<Autocomplete
  name="with-helper"
  label="Country"
  helperText="Start typing to search for countries"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
/>

Required

<Autocomplete
  name="required-field"
  label="Required Field"
  required
  helperText="This field is required"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
/>

Disabled

<Autocomplete
  name="disabled-field"
  label="Disabled Field"
  disabled
  value={value}
  options={options}
  handleSelect={handleSelect}
/>

Read Only

<Autocomplete
  name="readonly-field"
  label="Read Only Field"
  readonly
  value="Selected Value"
  options={options}
  handleSelect={handleSelect}
/>

Feedback States

// Error state
<Autocomplete
  name="error-field"
  label="Field with Error"
  feedback="error"
  helperText="Please select a valid option"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
/>

// Success state
<Autocomplete
  name="success-field"
  label="Field with Success"
  feedback="success"
  helperText="Valid selection"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
/>

No Results Found

<Autocomplete
  name="with-notfound"
  label="Search"
  placeholder="Type to search"
  value={value}
  options={filteredOptions.length > 0 ? filteredOptions : [{ value: 'false', label: 'false' }]}
  handleSelect={handleSelect}
  onChange={handleChange}
  notFound="No items found matching your search"
/>

Props

name
string
Name attribute of the input element, used to link with label
label
string
Text displayed above the autocomplete field
value
string
Current value of the input field
options
OptionProps[]
Array of options to display in the dropdown. Each option has:
  • value: string - The option’s value
  • label: string - The display text
onChange
React.ChangeEventHandler<HTMLInputElement>
Callback fired when the input value changes
handleSelect
(option: OptionProps) => void
Callback fired when an option is selected from the dropdown
placeholder
string
Placeholder text shown when input is empty
size
'medium' | 'mediumX'
default:"mediumX"
Height of the autocomplete field
position
'bottom' | 'top'
Position where the options menu appears relative to the input
helperText
string
Auxiliary text displayed below the autocomplete
feedback
'success' | 'error'
Visual feedback state for validation
required
boolean
default:"false"
If true, displays an asterisk and marks the field as required
disabled
boolean
default:"false"
If true, disables the autocomplete field
readonly
boolean
default:"false"
If true, prevents editing but allows opening the dropdown
notFound
string
Message displayed when no options match the search. Use with options array containing { value: 'false', label: 'false' }
brand
BrandTypes
Brand theme to apply (avon, natura, theBodyShop, etc.)
className
string
Optional CSS class name for custom styling
testID
string
Optional ID for testing purposes
accessibilityInput
AriaAttributes
ARIA attributes for the input element
accessibilityLabel
AriaAttributes
ARIA attributes for the label element

Complete Example

import React, { useState, useEffect } from 'react'
import { Autocomplete, OptionProps } from '@naturacosmeticos/natds-react'

function CountrySelector() {
  const [value, setValue] = useState('')
  const [filteredOptions, setFilteredOptions] = useState<OptionProps[]>([])
  const [error, setError] = useState('')

  const countries = [
    { value: 'us', label: 'United States' },
    { value: 'ca', label: 'Canada' },
    { value: 'mx', label: 'Mexico' },
    { value: 'br', label: 'Brazil' },
    { value: 'ar', label: 'Argentina' },
    { value: 'uk', label: 'United Kingdom' },
    { value: 'de', label: 'Germany' },
    { value: 'fr', label: 'France' },
    { value: 'es', label: 'Spain' },
    { value: 'it', label: 'Italy' }
  ]

  useEffect(() => {
    setFilteredOptions(countries)
  }, [])

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const query = e.target.value
    setValue(query)
    setError('')

    if (query === '') {
      setFilteredOptions(countries)
      return
    }

    const filtered = countries.filter(country =>
      country.label.toLowerCase().includes(query.toLowerCase())
    )

    setFilteredOptions(
      filtered.length > 0
        ? filtered
        : [{ value: 'false', label: 'false' }]
    )
  }

  const handleSelect = (option: OptionProps) => {
    if (option.value !== 'false') {
      setValue(option.label)
      setFilteredOptions(countries)
      setError('')
    }
  }

  const handleBlur = () => {
    const isValid = countries.some(
      country => country.label.toLowerCase() === value.toLowerCase()
    )
    if (value && !isValid) {
      setError('Please select a valid country from the list')
    }
  }

  return (
    <Autocomplete
      name="country"
      label="Country"
      placeholder="Search for a country"
      required
      value={value}
      options={filteredOptions}
      handleSelect={handleSelect}
      onChange={handleChange}
      feedback={error ? 'error' : undefined}
      helperText={error || 'Start typing to search for countries'}
      notFound="No countries match your search"
      accessibilityInput={{
        'aria-label': 'Search for country',
        'aria-autocomplete': 'list',
        'aria-required': true,
        onBlur: handleBlur
      }}
    />
  )
}

Accessibility

Built-in Features

  • Keyboard navigation (Arrow keys, Enter, Escape)
  • ARIA autocomplete attributes
  • Focus management
  • Screen reader announcements
  • Proper role attributes

Best Practices

<Autocomplete
  name="accessible-autocomplete"
  label="Search Products"
  placeholder="Type to search"
  value={value}
  options={options}
  handleSelect={handleSelect}
  onChange={handleChange}
  accessibilityInput={{
    'aria-label': 'Search for products',
    'aria-autocomplete': 'list',
    'aria-expanded': menuOpen,
    'aria-activedescendant': activeOptionId
  }}
  accessibilityLabel={{
    'aria-live': 'polite'
  }}
/>

Guidelines

  • Provide clear labels and placeholders
  • Show “no results” message when applicable
  • Use appropriate ARIA attributes
  • Ensure keyboard navigation works smoothly
  • Provide feedback for selections
  • Handle focus states properly
  • Support Escape key to close menu
  • Announce results to screen readers

TypeScript

import { AutocompleteProps, OptionProps } from '@naturacosmeticos/natds-react'

interface SearchableFieldProps {
  fieldName: string
  fieldLabel: string
  allOptions: OptionProps[]
  onSelect: (value: string) => void
}

const SearchableField: React.FC<SearchableFieldProps> = ({
  fieldName,
  fieldLabel,
  allOptions,
  onSelect
}) => {
  const [value, setValue] = useState('')
  const [options, setOptions] = useState(allOptions)

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const query = e.target.value
    setValue(query)

    const filtered = allOptions.filter(opt =>
      opt.label.toLowerCase().includes(query.toLowerCase())
    )

    setOptions(filtered.length > 0 ? filtered : [{ value: 'false', label: 'false' }])
  }

  const handleSelect = (option: OptionProps) => {
    if (option.value !== 'false') {
      setValue(option.label)
      onSelect(option.value)
    }
  }

  return (
    <Autocomplete
      name={fieldName}
      label={fieldLabel}
      value={value}
      options={options}
      handleSelect={handleSelect}
      onChange={handleChange}
      notFound="No matches found"
    />
  )
}
  • Select - For simpler dropdowns with fewer options
  • TextField - For free-form text input
  • Input - Lower-level input component

Build docs developers (and LLMs) love