From a postal code
Type a postal code and the selects fill in: all three levels for a code with one neighbourhood, province and district for a shared one.
Instead of picking from the selects, the address can also be filled from a postal code. Add a postal code input and the selects fill themselves once a code is typed. Pick a neighbourhood and the same input shows the derived code, so both directions work together.
The postal code input
Address line
Builds up as you pick
value
{
"provinceCode": null,
"province": null,
"district": null,
"neighbourhood": null,
"postalCode": null,
"postalCodes": []
}getPostalCodeInputProps() spreads onto a plain <input>: inputMode="numeric", autoComplete="postal-code", a maxLength of 5, value and onChange. Anything that is not a digit is dropped, and the lookup runs once five digits are in. While the code is being edited with fewer digits, the address stays where it is.
With names.postalCode set, the input carries that name. Don't also render the hidden getPostalCodeProps() input in the same form; both would submit the same field.
How certain is it?
Every postal code belongs to one district, and its first two digits are the province's plate code. But a code often covers several neighbourhoods: only 205 of 2,777 codes have exactly one, while 54100 in Sakarya is shared by 81. So the lookup fills in what the code can say for certain, and postalCodeStatus tells you what it found:
| postalCodeStatus | What happened |
|---|---|
"exact" | One neighbourhood under the code: province, district and neighbourhood are filled. |
"partial" | Province and district are filled; the neighbourhood list is narrowed to the code and the user picks one. A neighbourhood already picked under the code is kept. |
"unknown" | Not a code in the dataset. The address does not change and the input gets aria-invalid. |
"loading" | The code's province is loading. Only that one province is read. |
null | No lookup is active: no code was typed, or the province or district was changed by hand. |
The data build checks that every code stays within one district and starts with its plate code; a refresh that breaks either rule does not ship.
Behaviour
- Picking a province or district by hand drops the lookup: the neighbourhood list is complete again and the input shows the code derived from the pick.
- Picking a neighbourhood from the narrowed list keeps the lookup. For a neighbourhood with several codes,
value.postalCodeis the one the user typed, not the first. - If another pick is made or a new code typed while a code is loading, the older response never lands on top of the newer one.
- A province already loaded for the selects is not downloaded again; the lookup reads the same data.
- With a
form, every level filled is written withshouldDirty; for"partial"the neighbourhood is empty and the postal code filled.form.reset()drops the lookup too.
setPostalCode()
Start a lookup yourself, without the input, for a code from a saved address or a location. The promise resolves with what the code matched:
const { setPostalCode, value } = useViraAddress()
await setPostalCode("34710") // "exact"
value
// { province: "İSTANBUL", district: "KADIKÖY",
// neighbourhood: "CAFERAĞA MAH.", postalCode: "34710", ... }
await setPostalCode("34096") // "partial"
value
// { province: "İSTANBUL", district: "FATİH",
// neighbourhood: null, postalCode: "34096", ... }
await setPostalCode("34999") // "unknown": the address stays as it was
await setPostalCode(null) // null: drops the lookup, keeps the addressPass the code as text: 01040 as a number is 1040, and the leading zero cannot be brought back. Spaces are dropped, so "34 710" works.
UI libraries
The props are standard input props, and onChange takes either an event or a bare value. shadcn/ui's and Ant Design's Input take them as they are:
import { Input } from "@/components/ui/input"
<Input
{...address.getPostalCodeInputProps({ placeholder: "34710" })}
aria-describedby="postal-code-hint"
/>On the server
The same lookup runs on the server with resolvePostalCode, and resolveAddress checks a submitted postal code against the neighbourhood. See server validation. For the raw result without React, use findPostalCode from the dataset.
