RHF-MUI Components
NPM

RHFCountrySelect

RHFCountrySelect is an Autocomplete pre-loaded with a list of 250 countries (flag, name, ISO and ISO3 code). Store the complete CountryDetails object, or a single property via valueKey. Pin common choices with preferredCountries.

Usage

import RHFCountrySelect, {
  RHFCountrySelectProps,
  CountryISO,
  CountryDetails,
  countryList
} from '@nish1896/rhf-mui-components/mui/country-select';

Without a valueKey, the stored value is the whole CountryDetails object, or null when nothing is selected:

import { useForm } from 'react-hook-form';

type FormValues = {
  country: CountryDetails | null;
};

const { control, handleSubmit } = useForm<FormValues>({
  defaultValues: { country: null }
});

<form onSubmit={handleSubmit(values => console.log(values.country?.name))}>
  <RHFCountrySelect
    fieldName="country"
    control={control}
    registerOptions={{ required: 'Please select a country' }}
  />
</form>

onValueChange receives { newValue, event, reason, details }: the value that was stored, the original change event, and Material UI's change reason and details.

The label, helper text and error props work as described in RHFTextField.

Country data

Each country is an object with a name, an iso code, an iso3 code and an emoji flag:

{
  name: 'India',
  iso: 'IN',
  iso3: 'IND',
  emoji: '🇮🇳'
}

The iso key is strongly typed as CountryISO, which keeps preferredCountries and your own code type safe. The full list of 250 countries is exported as countryList, so you can read or filter it yourself.

Choosing the stored value

Set valueKey to store a single property of the country instead of the whole object. It can be iso, iso3 or name.

valueKeyStored value for India
none{ name: 'India', iso: 'IN', iso3: 'IND', emoji: '🇮🇳' }
iso'IN'
iso3'IND'
name'India'
const { control } = useForm<{ nationality: string | null }>({
  defaultValues: { nationality: null }
});

/* stores "IND" for India, "AUS" for Australia */
<RHFCountrySelect
  fieldName="nationality"
  control={control}
  valueKey="iso3"
/>

The stored value is also how a country is matched. With a valueKey, a country is matched by that property, and a stored value that isn't in the list isn't shown as selected.

When valueKey is omitted, countries are matched by iso, so an object loaded from your API matches as long as its iso is the same. Clearing the field stores null, or [] with multiple, unless disableClearable is set.

Customizing the country list

By default every country in countryList is listed in the options. Pass the countries prop to render only the ones you need.

const filteredCountries = countryList.filter(country => (
  ['IN', 'US', 'AU', 'FI', 'UA', 'CN', 'GB', 'JP', 'VN'].includes(country.iso)
));

preferredCountries pins countries to the top of the dropdown, by iso code. They appear in the order you list them, followed by the remaining countries in their usual order. A pinned country isn't repeated further down, and a code that isn't in the list is ignored. It only changes the order of the options, not the value.

<RHFCountrySelect
  fieldName="dreamDestinations"
  control={control}
  countries={filteredCountries}
  preferredCountries={['IN', 'AU', 'JP']}
  valueKey="name"
  multiple
/>

Multiple selection

Set multiple to store an array: of whole country objects, or of the valueKey property when one is set. Use [] as the default value. Each selection is shown as a chip, and the menu stays open while the user picks.

While the field is not focused, only the first limitTags chips (default 2) are shown, followed by a chip such as "+1 Country" or "+3 Countries". limitTags={-1} shows all of them, and getLimitTagsText receives the number of hidden chips and returns your own text. ChipProps customizes the chips.

const { control } = useForm<{ countriesVisited: string[] }>({
  defaultValues: { countriesVisited: [] }
});

<RHFCountrySelect
  fieldName="countriesVisited"
  control={control}
  valueKey="iso"
  multiple
  limitTags={3}
  getLimitTagsText={hidden => `+${hidden} more`}
  ChipProps={{ color: 'primary', size: 'small' }}
/>

Custom option labels

Each option shows the country's flag and name by default. renderOptionLabel replaces that content. It receives the country and a state object with selected, index, inputValue and disabled.

<RHFCountrySelect
  fieldName="country"
  control={control}
  renderOptionLabel={(option, { selected }) => (
    <span style={{ fontWeight: selected ? 700 : 400 }}>
      {option.emoji} {option.name} ({option.iso3})
    </span>
  )}
/>

Input and behavior

textFieldProps is forwarded to the underlying TextField, for example a placeholder, variant or size. autoHighlight is true by default, so the first match is highlighted as the user types and Enter selects it. disableClearable removes the clear button. Only countries from the list can be selected, so freeSolo is not supported.

Handling value changes

onValueChange runs after the new value has been stored in the form. It receives { newValue, event, reason, details }:

<RHFCountrySelect
  fieldName="country"
  control={control}
  valueKey="iso"
  onValueChange={({ newValue, event, reason, details }) => {
    setValue('state', '')
  }}
/>

Use customOnChange when you need to change what gets stored. It replaces the default handling, so you must call rhfOnChange yourself with the value to store. This example limits a multiple select to five countries: onValueChange is not called when customOnChange is used.

<RHFCountrySelect
  fieldName="countriesVisited"
  control={control}
  valueKey="iso"
  multiple
  customOnChange={({ rhfOnChange, newValue, event, reason, details }) => {
    if (newValue.length <= 5) {
      rhfOnChange(newValue);
    }
  }}
/>

Examples

API

The RHFCountrySelectProps interface extends AutocompleteProps and accepts the following additional props. Props marked with * are required.

NameTypeDescription
fieldName*string
Name of the field registered with React Hook Form. This prop is required for all components.
control*UseFormControl
The control option yielded on calling the useForm hook.
registerOptionsRegisterOptions
React Hook Form validation rules. Useful when not using a schema validation library such as Yup or Joi.
countriesCountryDetails[]
The list of countries to render for selection in the Autocomplete. By default all countries will be listed.
preferredCountriesCountryISO[]
The countries to show at the top of the list. The array requires the iso values of the countries.
valueKey`name` / `iso` / `iso3`
Determines which country property is stored as the selected value. If not provided, the complete country object is returned.
multipleboolean
Allow selection of more than one countries
customOnChange({ rhfOnChange, newValue, event, reason, details? }) => void
Custom change handler that overrides the default update behavior. Receives the updated value, the triggering event, and rhfOnChange. Use this to validate, transform, or conditionally update country selections before updating form state. When provided, you must call rhfOnChange manually.
onValueChange({ newValue, event, reason, details? }) => void
Called when the selected country value changes. Returns the selected country property defined by valueKey, or the complete country object when valueKey is not provided. Returns an array when multiple is enabled and null when no selection is made.
labelReactNode
The text to render in the FormLabel component. By default, the value of fieldName is transformed (e.g., "firstName" to "First Name") using the fieldNameToLabel function.
showLabelAboveFormFieldboolean
Render form label above the form field in FormLabel component.
formLabelPropsFormLabelProps
FormLabelProps to customise FormLabel component for a field. Multiple fields can be configured using the ConfigProvider component.
hideLabelboolean
Hides the FormLabel component if you don’t want to display the default form label component or prefer to render a fully custom label instead.
renderOptionLabel(option: CountryDetails, state) => ReactNode
Custom renderer for each country option in the dropdown. Receives the country object and should return the label/content to render. Option state param added in v4.2.0.
requiredboolean
Indicates that the field is mandatory by adding an asterisk symbol (*) to the formLabel. This visual cue helps users quickly identify required fields in the form.
renderError(error: FieldError) => ReactNode
Custom renderer for the React Hook Form field error. Receives the current field error and returns the content to display, such as error.message or a custom React element in the HelperText component.
Added in v4.1.0.
hideErrorMessageboolean
A flag to prevent replacement of helper text of a field by the errorMessage when the validation is triggered.
helperTextReactNode
The content to display within the FormHelperText component below the field. If the field validation fails, this content will be overridden by the corresponding error message.
formHelperTextPropsFormHelperTextProps
FormHelperTextProps to customise FormHelperText component for a field. Multiple fields can be configured using the ConfigProvider component.
textFieldPropsTextFieldProps
Props to customise the Autocomplete Textfield.
ChipPropsChipProps
Props applied to the Chip component used to render selected values.
customIds{ field, label, helperText, error }
Overrides the default field, label, helper text, and error IDs used for accessibility.

Source Code

View the full implementation of this component on GitHub.