City Selector
A ready-made province and city picker for Iran. Built on Combobox, and bundled with all 31 provinces and 1,119 cities — the city field stays disabled until a province is chosen, and both are searchable.
Last updated August 6, 2026
Overview
Examples
Controlled (English)
No city selected.
Controlled (Persian)
شهری انتخاب نشده است.
Locale
By default, labels follow the document's text direction — Persian in RTL, English in LTR. Pass locale to force one regardless of direction — it flips the text direction of the picker too.
locale="fa"
locale="en"
Custom layout
Compose CitySelectorProvince and CitySelectorCity yourself as children of CitySelector for full control over layout, labels, and placeholders.
Form
CitySelector is a plain controlled component — its value / onValueChange pair binds directly into any state or validator, no adapter needed. Here it's validated with Zod alongside a plain text field.
Installation
$ npx shadcn@latest add @persianlabsui/city-selectorThis also installs components/ui/combobox.tsx and lib/persian-provinces.ts, the bundled provinces and cities dataset.
Usage
import { CitySelector } from "@/components/ui/city-selector"
export function Example() {
return <CitySelector />
}Or compose the parts yourself for full control over layout, labels, and placeholders:
import { Label } from "@/components/ui/label"
import {
CitySelector,
CitySelectorCity,
CitySelectorProvince,
} from "@/components/ui/city-selector"
export function Example() {
return (
<CitySelector locale="en" className="flex-col gap-4 sm:flex-col">
<div className="flex flex-col gap-1.5">
<Label>Province</Label>
<CitySelectorProvince placeholder="Choose a province…" />
</div>
<div className="flex flex-col gap-1.5">
<Label>City</Label>
<CitySelectorCity placeholder="Choose a city…" />
</div>
</CitySelector>
)
}API Reference
CitySelector
| Prop | Type | Default | Description |
|---|---|---|---|
| value | CitySelectorValue | undefined | The selected province and city. Use when controlled. |
| defaultValue | CitySelectorValue | { province: null, city: null } | The initially selected province and city when uncontrolled. |
| onValueChange | (value: CitySelectorValue) => void | — | Called when the province or city changes. Selecting a new province resets the city. |
| locale | "fa" | "en" | auto | Forces Persian or English labels, placeholders, and text direction. When omitted, follows the document's text direction (rtl → fa, ltr → en). |
| disabled | boolean | false | Disables both the province and city comboboxes. |
| children | ReactNode | <CitySelectorProvince /><CitySelectorCity /> | Pass CitySelectorProvince and CitySelectorCity yourself to fully customize layout, labels, and placeholders. |
CitySelectorProvince
| Prop | Type | Default | Description |
|---|---|---|---|
| placeholder | string | locale default | Overrides the province input's placeholder text. |
| className | string | undefined | Forwarded to the underlying ComboboxInputGroup. |
CitySelectorCity
| Prop | Type | Default | Description |
|---|---|---|---|
| placeholder | string | locale default | Overrides the city input's placeholder text. |
| className | string | undefined | Forwarded to the underlying ComboboxInputGroup. |