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.
valueKey | Stored 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.
| Name | Type | Description |
|---|---|---|
fieldName* | string | Name of the field registered with React Hook Form. This prop is required for all components. |
control* | UseFormControl | |
registerOptions | RegisterOptions | React Hook Form validation rules. Useful when not using a schema validation library such as Yup or Joi. |
countries | CountryDetails[] | The list of countries to render for selection in the Autocomplete. By default all countries will be listed. |
preferredCountries | CountryISO[] | 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. |
multiple | boolean | 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. |
label | ReactNode | 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. |
showLabelAboveFormField | boolean | Render form label above the form field in FormLabel component. |
formLabelProps | FormLabelProps | FormLabelProps to customise FormLabel component for a field. Multiple fields can be configured using the ConfigProvider component. |
hideLabel | boolean | 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. |
required | boolean | 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. |
hideErrorMessage | boolean | A flag to prevent replacement of helper text of a field by the errorMessage when the validation is triggered. |
helperText | ReactNode | 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. |
formHelperTextProps | FormHelperTextProps | FormHelperTextProps to customise FormHelperText component for a field. Multiple fields can be configured using the ConfigProvider component. |
textFieldProps | TextFieldProps | Props to customise the Autocomplete Textfield. |
ChipProps | ChipProps | 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.

