useViraAddress()
The hook API: options, the return value, the value object and bringing your own data.
Basic usage
The hook returns three lists and three setters. Every list item is { value, label }: value is the name as the dataset spells it (FATİH), label is the display form (Fatih). For provinces, value is the plate code ("34").
const {
provinces, districts, neighbourhoods,
setProvince, setDistrict, setNeighbourhood,
value, loading,
} = useViraAddress()
return (
<>
<select
value={value.provinceCode ?? ""}
onChange={(e) => setProvince(e.target.value)}
>
<option value="">Select a province</option>
{provinces.map((p) => (
<option key={p.value} value={p.value}>{p.label}</option>
))}
</select>
<select
value={value.district ?? ""}
onChange={(e) => setDistrict(e.target.value)}
disabled={!districts.length}
>
<option value="">Select a district</option>
{districts.map((d) => (
<option key={d.value} value={d.value}>{d.label}</option>
))}
</select>
<select
value={value.neighbourhood ?? ""}
onChange={(e) => setNeighbourhood(e.target.value)}
disabled={!neighbourhoods.length}
>
<option value="">
{loading.neighbourhoods ? "Loading…" : "Select a neighbourhood"}
</option>
{neighbourhoods.map((n) => (
<option key={n.value} value={n.value}>{n.label}</option>
))}
</select>
</>
)The prop getters do the same wiring in one line. setProvince accepts the plate code as a number or a string.
value
Everything your form needs, in one object:
{
provinceCode: "34", // plate code, as a string
province: "İSTANBUL",
district: "FATİH",
neighbourhood: "AKSARAY MAH.",
postalCode: "34096", // the neighbourhood's first code, or the one typed
postalCodes: ["34096"], // all of them; a few hundred have several
}Levels that are not picked yet are null, and postalCodes is an empty array. Names are upper case, as the dataset stores them; show the label from the lists, or run them through formatName.
Options
| Option | Default | What it does |
|---|---|---|
source | the bundled dataset | Where the lists come from. Swap it for a test fixture or your own API. |
defaultValue | — | { provinceCode, district, neighbourhood } to start from. With a form, read from the form when omitted. |
formatLabel | formatName | Turns a raw name into a label; the second argument is the level. (name) => name keeps the dataset's upper case. |
onChange | — | Called with the whole value on every change, not on the first render. |
form | — | A form to keep in sync, such as React Hook Form's useForm(). |
names | province, district, neighbourhood, postalCode | Field names in the form and on the inputs the prop getters return. provinceCode is only written when you name it. |
shouldValidate | true | Revalidate the form fields as the address changes. |
form, names and shouldValidate are covered on the React Hook Form page.
Return value
| Field | Description |
|---|---|
value | The selected address; see above. |
provinces, districts, neighbourhoods | Ready-made { value, label } lists for a select. Neighbourhood options also carry postalCode and postalCodes. |
setProvince(code), setDistrict(name), setNeighbourhood(name) | Picks a level; null or "" clears it. A higher level clears the ones below. |
setPostalCode(code) | Fills the address from a postal code; resolves with "exact" | "partial" | "unknown". See from a postal code. |
postalCodeStatus | What the last postal code matched; null when no lookup is active. |
reset() | Clears every level. |
loading.neighbourhoods | true while the picked province's neighbourhoods load. |
error | The last load error. Cleared when another province is picked; picking the failed one again retries. |
getProvinceProps(), getDistrictProps(), getNeighbourhoodProps() | Props to spread on a plain <select>; disabled is worked out from the chain. Other components pick a flavour with as. |
getPostalCodeProps() | A hidden input that carries the derived postal code. |
getPostalCodeInputProps() | An input the user can type a postal code into; it also shows the code derived from a pick. |
Your own data source
By default the lists come from the bundled dataset. Pass a source to use a small fixture in tests, or to read the lists from your own API:
import { useViraAddress, type AddressSource } from "@virastack/address"
const aksaray = {
name: "AKSARAY MAH.",
postalCode: "34096",
postalCodes: ["34096"],
}
const source: AddressSource = {
provinces: [
{ code: 34, name: "İSTANBUL", districts: ["FATİH", "KADIKÖY"] },
],
neighbourhoods: async (code) => new Map([["FATİH", [aksaray]]]),
}
const address = useViraAddress({ source })The hook only reads neighbourhoods when the province changes, so passing a new source object on every render does not reload anything. If you only want the neighbourhoods to come from elsewhere, setLoader is shorter; see the dataset.
