Country, state, and city fields are some of the most annoying fields to get right in any signup, checkout, or address form. Most implementations fall into one of a few traps: a country dropdown hardcoded from a list someone copy-pasted years ago, a state field that's really just a free-text box because nobody wanted to maintain state data for 195 countries, or a form that just makes the user type all three by hand while you cross your fingers about data quality.
react-country-state-fields is a React package built to close that gap. It gives you cascading country, state, and city fields (pick a country, the state list narrows to that country, pick a state, the city list narrows to that state) with data for 250 countries, 5,299 states, and 153,765 cities already built in, so you're not maintaining that dataset yourself. It ships as Material UI components, or as headless hooks if you're using anything else.
And if you want to take it further, the fields can optionally auto-fill from the visitor's IP location, so users don't have to manually select their own country, state, and city at all, they just confirm or correct it. That's a genuine reduction in manual data entry at exactly the point in a form where people tend to slow down or bail, though how much it moves completion or conversion will depend on your form and audience.
Where this fits
A few places this shows up directly:
- Signup forms. A working country/state/city component means you're not hand-rolling a 195-item dropdown and a state list from scratch. Turn on auto-fill and new users don't have to select their own country at all, just confirm it.
- Checkout and shipping address forms. Country, state/province, and city are exactly the fields that slow down checkout the most when they're not cascading correctly or require the user to type everything manually. A correctly cascading component (with or without auto-fill) is a direct improvement here.
- Localization and personalization flows. If you're already tailoring pricing, currency, or content by region, the same location detection that powers that can pre-populate these fields too, instead of asking the user to enter something you may already know.
In all three cases, the fields stay fully user-editable. Auto-fill, when it's turned on, is a starting point, not a lock-in. Someone traveling, using a VPN, or just living somewhere their IP doesn't quite match can always correct it.
See it in action
Without autofill
That's the base cascade: pick a country, the state list narrows to that country, pick a state, the city list narrows to that state. The styled fields on the left are the built-in Material UI components; the plain fields on the right are built entirely on the headless hooks, sharing the same underlying state.
With autofill (optional)
Same fields, with auto-fill turned on: a brief loading state while the IP lookup runs, then Country, State, and City fill in on their own, then clicking the Country field opens the normal dropdown with every option still available. The auto-filled value is a starting point, not a locked-in one.
Turning on auto-fill (optional)
The fields work as a complete country/state/city component with no further setup: install the package, drop in the components, done. Auto-fill from the visitor's IP location is an enhancement on top of that, and it's opt-in.
To turn it on, you need a projectId from a free VisitorAPI project, passed as a prop on <VisitorAPIComponents>. Without a projectId, the fields still work perfectly as regular cascading country/state/city selects, they just won't auto-fill.
Getting a project ID takes a couple of minutes:
- Sign up at VisitorAPI, free for up to 1,000 requests a month, no credit card required.
- Create a project and add your app's domain to the allowlist.
- Copy the project ID from the project settings.
No API key is involved, VisitorAPI authorizes by domain instead, so it's safe to call directly from front-end code.
Getting started
Install the package:
npm i react-country-state-fields
Using the built-in MUI fields
If you're already using Material UI, this is the fastest path:
import { CountryField, StateField, CityField, VisitorAPIComponents } from 'react-country-state-fields';
export const AddressForm = () => (
<VisitorAPIComponents projectId="<visitorapi-project-id>">
<CountryField label="Country/Territory" />
<StateField label="State/Province" />
<CityField label="City" />
</VisitorAPIComponents>
);
That's the whole form. Cascading and validation-friendly fallback (a free-text input for the countries without state-level data) are handled for you. Leave out projectId (or the prop entirely) if you don't want auto-fill, the fields work the same way otherwise.
Using the headless hooks (no MUI required)
If you're not using MUI, import from the /headless entry point instead and build the UI yourself:
import { VisitorAPIComponents, useCountryField } from 'react-country-state-fields/headless';
function CountrySelect() {
const { value, options, onChange } = useCountryField();
return (
<select value={value?.code ?? ''} onChange={(e) => onChange(e.target.value)}>
<option value="">Select a country</option>
{options.map((c) => <option key={c.code} value={c.code}>{c.label}</option>)}
</select>
);
}
export const AddressForm = () => (
<VisitorAPIComponents projectId="<visitorapi-project-id>">
<CountrySelect />
</VisitorAPIComponents>
);
useStateField() and useCityField() follow the same shape for the other two fields, and useVisitorLocationStatus() gives you { loading, error } if you want to show a spinner or fallback state while auto-fill is in flight. <VisitorAPIComponents> is the same provider either way, so headless and MUI fields can even be mixed under one provider if you're migrating gradually.
Try it
npm i react-country-state-fields
- View the repo on GitHub for the full README, prop reference, and a working example app you can clone and run.
- Create a free VisitorAPI project if you want to try auto-fill, no credit card required for up to 1,000 requests a month.


Top comments (0)