
State Dropdown List: HTML, React & Best Practices
Someone on the team says, “Can you just add a state dropdown to the address form?” That request sounds tiny until you touch the implementation.
A basic <select> takes minutes. A production-ready state dropdown list takes more thought. You have to decide whether a dropdown is even the right control, wire it for screen readers, keep the label clear, support state-specific logic, and avoid baking fragile geography data into your frontend forever.
That's where most examples fall short. They stop at a copy-paste list of options. Real projects need more than that. They need a component that works in plain HTML, adapts in React, and stays maintainable when the form grows from “U.S. states only” into country-aware or program-specific flows.
The Deceptively Simple State Dropdown
The first version of a state field usually ships fast. It's often a bare <select> with a placeholder and a few copied <option> tags. It works in a local demo, and nobody complains until the form goes live.
Then the edge cases show up.
A mobile user has to scroll through a long list. A screen reader announces the field poorly because the label wasn't connected correctly. A product manager asks for Washington, D.C. A client asks why Puerto Rico isn't available. Then another market enters the picture and “state” becomes “province” or “region.”
What makes this field hard
A state selector sits at the intersection of UX, accessibility, and data modeling. That's why it causes more trouble than it looks like it should.
A few common failure modes show up over and over:
- The field is semantically weak. There's no proper
<label>, so assistive tech users get a worse experience. - The list is hardcoded forever. That's fine until the form needs territories, country switching, or eligibility logic.
- The wrong control gets chosen. Teams default to a dropdown when an autocomplete or validated text field would be faster.
- The form logic hides context. One select changes another and users lose track of what happened.
Practical rule: Treat geographic fields like product infrastructure, not decoration. They look small, but other systems depend on them.
The shift from snippet to component
The useful way to think about a state dropdown list is this: it starts as markup, but it ends as a maintained input system.
That means the implementation has to hold up in three places:
| Concern | What good looks like | What breaks in production |
|---|---|---|
| HTML | Proper label, clear default option, valid option values | Unlabeled fields, confusing placeholder text |
| UX | Fast input method, predictable behavior | Long scrolling, changing lists, unclear state |
| Data | Central source of truth, easy updates | Duplicated lists across apps and forms |
If you build the foundation cleanly, everything else gets easier. If you start with a messy snippet, every new requirement turns into a patch.
The Foundation A Pure HTML State Select
If you need a reliable baseline, start with semantic HTML. It's still the most portable version of a state dropdown list, and it degrades well across browsers, frameworks, and form builders.
A U.S. state selector typically needs all 50 states, and many implementations also add non-state options like Washington, D.C., depending on the workflow. The important part isn't just the options. It's the structure around them.

Start with semantic markup
Here's a copy-ready HTML example:
<div class="form-field">
<label for="state">State</label>
<select id="state" name="state" autocomplete="address-level1" required>
<option value="">Select a state</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
<option value="FL">Florida</option>
<option value="GA">Georgia</option>
<option value="HI">Hawaii</option>
<option value="ID">Idaho</option>
<option value="IL">Illinois</option>
<option value="IN">Indiana</option>
<option value="IA">Iowa</option>
<option value="KS">Kansas</option>
<option value="KY">Kentucky</option>
<option value="LA">Louisiana</option>
<option value="ME">Maine</option>
<option value="MD">Maryland</option>
<option value="MA">Massachusetts</option>
<option value="MI">Michigan</option>
<option value="MN">Minnesota</option>
<option value="MS">Mississippi</option>
<option value="MO">Missouri</option>
<option value="MT">Montana</option>
<option value="NE">Nebraska</option>
<option value="NV">Nevada</option>
<option value="NH">New Hampshire</option>
<option value="NJ">New Jersey</option>
<option value="NM">New Mexico</option>
<option value="NY">New York</option>
<option value="NC">North Carolina</option>
<option value="ND">North Dakota</option>
<option value="OH">Ohio</option>
<option value="OK">Oklahoma</option>
<option value="OR">Oregon</option>
<option value="PA">Pennsylvania</option>
<option value="RI">Rhode Island</option>
<option value="SC">South Carolina</option>
<option value="SD">South Dakota</option>
<option value="TN">Tennessee</option>
<option value="TX">Texas</option>
<option value="UT">Utah</option>
<option value="VT">Vermont</option>
<option value="VA">Virginia</option>
<option value="WA">Washington</option>
<option value="WV">West Virginia</option>
<option value="WI">Wisconsin</option>
<option value="WY">Wyoming</option>
<option value="DC">District of Columbia</option>
</select>
</div>Why this version holds up
Three details matter more than developers sometimes admit:
- The
<label>is connected. Thefor="state"andid="state"pairing gives the field a proper accessible name. - The placeholder is empty-valued.
value=""works well with native validation and letsrequireddo its job. - The option values are stable. Use abbreviations or another consistent canonical format. Don't mix full names and codes casually.
If you want a broader grounding in select controls, this guide to dropdown menus in forms is a useful companion.
What not to do in HTML
Avoid these shortcuts:
- Don't skip the label. Placeholder text is not a replacement for a visible label.
- Don't alphabetize poorly. Users scan by expected order. A sloppy list creates friction fast.
- Don't bury the field in custom UI too early. Native selects are ugly sometimes, but they're battle-tested.
Native form controls are often boring in the right way. Boring is good when the field collects address data.
For a U.S.-only form, this version is enough to ship. For anything more dynamic, keep the markup pattern and upgrade the data layer, not the semantics.
Building an Accessible and Usable Dropdown
A state dropdown usually fails in ordinary ways. The label is vague. The focus ring disappears under custom styling. The error message shows up visually but never reaches a screen reader. Users can still submit the form, but the field takes more effort than it should.
That is the standard I use for this control. The job is not to render a list of regions. The job is to help people choose the right one, quickly, with a keyboard, a screen reader, or a thumb on a phone.
That starts with language. The CDC's guidance on audience-appropriate terms is useful here because form labels are product copy, not implementation details. A label that is technically correct but unclear still slows completion, especially in forms tied to benefits, health, housing, or legal status according to the CDC communication guidance.

Label the field like a human would read it
“State” works in a U.S.-only checkout where the surrounding fields already make the context obvious. It breaks down once the form serves multiple countries, territories, or business rules.
Use labels that match the decision the user is making:
- Simple checkout form: “State”
- International address form: “State / Province / Region”
- Eligibility workflow: “State or territory of residence”
- Program form: “State where services are requested”
Helper text should carry real information, not repeat the label in smaller type. If it explains why the field matters, connect it with aria-describedby so assistive tech reads it at the right time.
<label for="region">State or territory of residence</label>
<p id="region-help">Select the jurisdiction used for eligibility review.</p>
<select
id="region"
name="region"
aria-describedby="region-help"
autocomplete="address-level1"
required
>
<option value="">Select an option</option>
</select>Keep native behavior unless you can replace all of it
Native <select> controls solve more problems than many design systems give them credit for. Keyboard support is built in. Screen readers already know how to announce them. Mobile browsers often provide a better picker UI than any custom component a team can ship under deadline pressure.
Custom selects are still sometimes justified. I use them for searchable region lists, heavy filtering, or designs that need grouped results and async loading. But a custom trigger plus a listbox is not a styling exercise. It becomes an accessibility project.
Check these behaviors before shipping:
- Keyboard access works from end to end. Users should be able to tab in, open the control, move through options, and confirm a choice without getting trapped.
- Focus stays visible. A polished UI that hides focus indicators is harder to use and harder to debug.
- The accessible name is correct. The label should be announced consistently in screen readers.
- Errors are attached to the field. Users need visible feedback and programmatic feedback when validation fails.
- Touch behavior is sane. On mobile, the tap target, scroll behavior, and zoom level matter as much as semantics.
If a design starts pushing past native behavior, this guide to JavaScript dropdown patterns for interactive menus is a better reference point than treating a custom select like a styled <div>.
Accessibility check: If your custom select cannot match native keyboard support and screen reader behavior, ship the native select.
Choose the control based on speed, not habit
A dropdown is common for state fields because the data set is fixed. That does not make it the best input every time.
For a short, stable list in a standard checkout, a native select is low risk and easy to maintain. For a long region list, especially in international forms, autocomplete often reduces scanning time. For internal tools with trained users, free text with strong validation can be faster than forcing selection from a menu.
Here is the trade-off I use in production:
| Input pattern | Best fit | Weak fit |
|---|---|---|
| Native select | Fixed region list, low complexity forms, strong mobile browser support | Flows where users would choose faster by typing |
| Autocomplete | Larger or multi-country region datasets with validation behind the field | Tiny lists where search adds extra interaction |
| Free text plus validation | Internal systems and expert users who know the expected format | Public-facing forms where bad input is common |
On mobile, I ask a blunt question: will opening the list be faster than entering two or three characters? If not, the dropdown is there because developers expect it, not because users benefit from it.
Good state dropdowns age well. They start with clear labels and native semantics, survive validation and accessibility review, and leave room for the dynamic country-aware logic that comes later.
Dynamic States Country-Aware Selects in React
Once your form supports more than one geography, hardcoded options become technical debt. A React component proves its worth in this situation.
The key is to separate display logic from location data. Don't scatter lists through multiple components. Keep one data source and render from it.

A simple data model
Start with a plain object keyed by country code:
const regionsByCountry = {
US: [
{ value: "AL", label: "Alabama" },
{ value: "AK", label: "Alaska" },
{ value: "AZ", label: "Arizona" },
{ value: "CA", label: "California" },
{ value: "NY", label: "New York" },
{ value: "TX", label: "Texas" },
{ value: "DC", label: "District of Columbia" }
],
CA: [
{ value: "AB", label: "Alberta" },
{ value: "BC", label: "British Columbia" },
{ value: "ON", label: "Ontario" },
{ value: "QC", label: "Quebec" }
],
AU: [
{ value: "NSW", label: "New South Wales" },
{ value: "VIC", label: "Victoria" }
]
};This example is intentionally short. In production, keep the full list in a shared module or fetch it from a maintained endpoint.
A country-aware component
Here's a practical React example using useState:
import { useMemo, useState } from "react";
const regionsByCountry = {
US: [
{ value: "AL", label: "Alabama" },
{ value: "AK", label: "Alaska" },
{ value: "AZ", label: "Arizona" },
{ value: "AR", label: "Arkansas" },
{ value: "CA", label: "California" },
{ value: "NY", label: "New York" },
{ value: "TX", label: "Texas" },
{ value: "DC", label: "District of Columbia" }
],
CA: [
{ value: "AB", label: "Alberta" },
{ value: "BC", label: "British Columbia" },
{ value: "ON", label: "Ontario" },
{ value: "QC", label: "Quebec" }
]
};
export default function AddressForm() {
const [country, setCountry] = useState("US");
const [region, setRegion] = useState("");
const regionOptions = useMemo(() => {
return regionsByCountry[country] || [];
}, [country]);
function handleCountryChange(event) {
const nextCountry = event.target.value;
setCountry(nextCountry);
setRegion("");
}
return (
<form>
<div>
<label htmlFor="country">Country</label>
<select
id="country"
name="country"
value={country}
onChange={handleCountryChange}
>
<option value="US">United States</option>
<option value="CA">Canada</option>
</select>
</div>
<div>
<label htmlFor="region">
{country === "US" ? "State" : "Province"}
</label>
<select
id="region"
name="region"
value={region}
onChange={(event) => setRegion(event.target.value)}
disabled={regionOptions.length === 0}
>
<option value="">
{country === "US" ? "Select a state" : "Select a province"}
</option>
{regionOptions.map((item) => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
</div>
</form>
);
}This gets you most of the way there. The country drives the available regions, and changing country resets the dependent field.
Avoid disorienting interacting menus
Dependent selects can become confusing fast. One common mistake is removing choices from the UI entirely when another field changes.
Nielsen Norman Group advises keeping unavailable states visible but grayed out rather than removing them entirely, which preserves context and reduces confusion as summarized in this dropdown implementation guide.
That advice matters when your UI has eligibility or shipping rules. If a state can't be selected, disabling it can be clearer than making it disappear.
const usRegions = [
{ value: "CA", label: "California", available: true },
{ value: "NY", label: "New York", available: false },
{ value: "TX", label: "Texas", available: true }
];
<select id="state" name="state">
<option value="">Select a state</option>
{usRegions.map((state) => (
<option
key={state.value}
value={state.value}
disabled={!state.available}
>
{state.label}
</option>
))}
</select>Keep the list shape stable when possible. Users notice when a menu changes underneath them.
When to fetch the list instead of bundling it
Hardcoding can work for a local app or a static site with narrow requirements. It gets brittle when the form must support:
- Territories and special jurisdictions
- State-specific documents or rates
- Localized labels
- Program logic tied to region codes
At that point, fetch the data or generate it from a shared backend source. The frontend should render the options, not become the master registry.
A good rule is simple: if product, legal, or operations teams care about the list, it shouldn't live only inside a component file.
Handling Submissions with Static Forms
The frontend isn't the whole job. A clean state dropdown list still has to submit useful data, and the submitted value needs to be something your backend or form service can process consistently.
That's one reason stable option values matter. If one form sends California and another sends CA, reporting and downstream workflows get messy fast.

Use names for humans and codes for systems
This is the pattern I prefer:
- Show human-readable labels in the UI.
- Submit canonical values in the form field.
- Normalize on the backend if multiple frontends submit the same concept.
Example:
<label for="state">State</label>
<select id="state" name="state" required>
<option value="">Select a state</option>
<option value="CA">California</option>
<option value="NY">New York</option>
<option value="TX">Texas</option>
</select>That keeps analytics, routing rules, and exports cleaner.
Think beyond the standard list
Real-world workflows increasingly need more than the standard state set. Some applications need states or territories, and some government or benefits flows already direct users to choose a state or territory rather than just a state, which highlights the need for dynamic data sourcing in this discussion of state and territory workflows.
That changes what gets submitted. You may need a more neutral field name like region, jurisdiction, or state_or_territory instead of state.
Here's a more adaptable version:
<label for="jurisdiction">State or territory</label>
<select id="jurisdiction" name="jurisdiction" required>
<option value="">Select an option</option>
<option value="CA">California</option>
<option value="DC">District of Columbia</option>
<option value="PR">Puerto Rico</option>
</select>If you're embedding forms into a static site, this walkthrough on how to embed an HTML contact form is a practical reference for wiring the whole submission flow.
Submission details that save trouble later
A common tendency involves focusing on the visible field and overlooking operational details. Those details matter more over time.
A dependable submission flow should answer these questions:
| Concern | Good practice |
|---|---|
| Field naming | Use one canonical name across all forms |
| Stored value | Submit consistent codes, not mixed formats |
| Conditional logic | Keep region rules out of inline template hacks |
| Expansion | Leave room for territories and non-U.S. regions |
Your form payload is part of your API, even if you never call it that.
Once that mindset clicks, the implementation changes. The field stops being “just a select” and becomes a stable contract between the browser, your processing layer, and anyone reading exports later.
Beyond the Basics Validation and Maintenance
A state dropdown usually breaks long after the first release. The markup works, submissions go through, and then the edge cases start showing up. One form sends CA, another sends California, a third adds N/A, and reporting turns into cleanup work.
Validation should prevent that drift.
Browser validation is still the right starting point for required selects. Use required, keep the placeholder option empty, and let the platform block the obvious miss. It is fast, accessible by default, and easier to maintain than custom JavaScript for a basic selection rule.
Validation that respects the user
Good validation timing matters as much as the rule itself. Show feedback after the user has interacted with the field or on submit. Firing an error on focus is noisy. Waiting until the backend rejects the form is worse.
A production-ready validation stack usually includes:
- Native constraint validation for required selection checks
- Custom validation messages for country-specific or business-specific rules
- Server-side validation to reject invalid or unexpected values before storage
Server-side checks matter even for a plain <select>. Frontend constraints are easy to bypass, and stale clients are a practical concern. If your API accepts state=Californiia, that bug is now in your database, exports, and downstream automations.
Typed inputs need one extra rule. Validate the stored value, not the visible label. If the component displays "California" but stores CA, your validation and backend should treat CA as the contract.
Maintenance is where quality holds or slips
The hard part is not writing the dropdown. The hard part is keeping every dropdown in sync across products, countries, and teams.
Treat region data like application data, not template copy. Keep one canonical dataset for labels, codes, and country membership. Feed both your server and your frontend from that source. In React, that usually means importing a shared config or fetching region metadata from an endpoint instead of hardcoding options inside multiple components.
A small policy avoids a lot of cleanup later:
- Store one canonical value format, usually short codes
- Keep display labels separate from submitted values
- Name the field for the broader use case, such as
regionorstate_or_territorywhen needed - Review the control choice as the list grows
That last point gets ignored. A dropdown is fine for a short, stable list. It becomes slower when users scroll through long region lists or when the form needs to support multiple countries with different administrative levels. At that point, a combobox or autocomplete with validated results is often the better UI. As noted earlier, dropdown usability research supports that trade-off for longer geographic lists.
A production mindset
Reliable implementations tend to share the same habits.
- Semantic HTML comes first
- Accessibility survives styling changes
- Option data has an owner
- Validation runs on both client and server
- The UI control matches the size and complexity of the dataset
That is what separates a copied snippet from a field you can keep shipping for years.
If you want to put a production-ready state dropdown list into a real form without building your own backend, Static Forms is a fast way to handle submissions from HTML, React, Next.js, Webflow, WordPress, and other modern stacks. You point your form to a hosted endpoint, keep your frontend simple, and get reliable submission handling with spam protection, file uploads, webhooks, email delivery, and privacy-friendly controls.
Related Articles
React Form Submission: Master Patterns & Validation
React form submission - Master React form submission for 2026! Our guide covers patterns, validation, async requests, file uploads, and integrations. Start
Form Submit jQuery: A Practical Guide for 2026
Master form submit jquery techniques for 2026. Learn AJAX, validation, and file uploads to build seamless, interactive web forms with this practical guide.
Mastering label tags html for Accessible Forms
Learn how to use label tags html correctly with practical examples. Master the 'for' attribute, nesting, accessibility, and usage in React and Vue.