DEV Community

Cover image for React Country, State, and City Field Component That Detects User Location and Autofills
Chaoming Li
Chaoming Li

Posted on • Originally published at visitorapi.com

React Country, State, and City Field Component That Detects User Location and Autofills

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

Country, state, and city fields cascading through manual selection, including the headless (no MUI) variant staying in sync

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)

Auto-fill demo: fields show a Detecting your location loading state, then Country, State, and City fill in automatically from the visitor's IP (Australia, New South Wales, Sydney), then a click on the Country field opens the full dropdown with the auto-filled value highlighted, confirming the field is still editable

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:

  1. Sign up at VisitorAPI, free for up to 1,000 requests a month, no credit card required.
  2. Create a project and add your app's domain to the allowlist.
  3. 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
Enter fullscreen mode Exit fullscreen mode

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>
);
Enter fullscreen mode Exit fullscreen mode

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>
);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (0)