Romanian Address Format: The Rules and a React Form
Search for an address form React example and you get a US layout: street, city, state, ZIP. Ship that to customers in Romania and they have nowhere to put the block, the staircase or the Bucharest sector, so the courier calls to ask. This tutorial covers the Romanian address format part by part, checked against the Universal Postal Union's sheet for Romania, with English labels and Romanian data. Then it builds the form in React on Address Input, a component from Wingo UI, the library we build, so we are not neutral.
What is the Romanian address format?
A Romanian address runs from the most specific line to the broadest: the street line with the building details, the sector in Bucharest, then the postal code and the locality. The UPU addressing sheet for Romania (opens in a new tab) (dated October 2007) gives four rules:
- The street type comes before the name: Str. (strada), Bd. (bulevardul), Calea, Aleea, Șos. (șoseaua).
- The number usually ends the street line.
- Building details can go at the end of the same line: block, staircase, floor, apartment.
- In Bucharest, the sector can sit on its own line between the street and the locality.
The 6-digit postal code goes to the left of the locality. Here is a Romania address format example from the sheet for an apartment in Bucharest, and one for an office in Craiova:
The sheet writes Valcii for Vâlcii, which matters for search later. Both stop at the locality, since the postal code implies the county. In domestic mail, invoices and courier forms, people add the county (județ) after the locality as jud. Cluj, which also tells apart villages that share a name. Here is the Romanian postal address format as form fields, with English labels:
The labels are Address Input's English defaults. We label the scară "Entrance" because each staircase of a block has its own door, which is what a courier looks for.
How do you write a Bucharest or village address?
In Bucharest the sector takes the place of the county, and in the countryside a village usually needs its commune as well. București is a municipality with the rank of a county, split into six sectors. Written on one line, as a courier form or an invoice prints it, a Bucharest address ends with "Sector 6, 061357 București" and has no jud. part.
Villages add a level. Counties contain municipalities (municipii), towns (orașe) and communes (comune), and a commune groups several villages (sate). The National Institute of Statistics keeps the official list as SIRUTA, a register on those three levels that it publishes as a CSV on data.gov.ro (opens in a new tab). A village address names the village and usually its commune, as in "sat Vâlcele, com. Feleacu, jud. Cluj": Feleacu (opens in a new tab) is a commune of five villages, one of them Vâlcele. In many rural communities (opens in a new tab) there are no street names at all.
RoAddress, the value type, has no commune field. Keep the village in locality and its SIRUTA code in localityId: the register links every village to its commune, so you can print the commune when a courier needs it.
How do Romanian postal codes relate to counties?
The first two digits of a Romanian postal code identify the county, or the sector in Bucharest. Romania moved to 6-digit codes on May 1, 2003, and the UPU sheet reads them as a postal region, a district of that region (or a sector in Bucharest), then the delivery area. Per Wikipedia's article on postal codes in Romania (opens in a new tab), cities of more than 50,000 people get a code per street or small group of addresses, a smaller town has one code for the whole town, and village codes have a 7 as their third digit, like Feleacu's 407270. Poșta Română's postal code search (opens in a new tab) finds the full code for an address.
Here is the Romanian counties list with the ISO 3166-2:RO (opens in a new tab) code (the letters on license plates) and the postal prefix. Address Input stores the county as this code, and the prefixes match its RO_POSTAL_PREFIXES:
That gives a cheap check. If someone picks Iași and types a code that starts with 40, one of the two is wrong: postalCodeMatchesCounty("400114", "IS") returns false. In Bucharest a chosen sector narrows it to one prefix, so postalCodeMatchesCounty("061357", "B", "6") is true and the same code with sector "3" is false.
Address Input shows a mismatch as an amber warning under the postal code once the field loses focus, and the form stays valid. We chose a warning on purpose: the check only knows prefixes, and a person who knows their own code should never be stuck at checkout.
How do I build a Romanian address form in React?
Split it into a county select, a locality search, street and number, an optional block row and a six-digit postal code, then validate on submit and again on the server. Address Input ships those parts. It is a Pro component, so sign in once with npx wingo-ui@latest login on a Pro account, then run npx wingo-ui@latest add address-input. The CLI adds what it builds on, including ro-locations, text-match, the select and the combobox. The component renders the county, the locality, street and number, a collapsed row for block, staircase, floor and apartment, the postal code and a details field for the intercom or a landmark. Its labels follow the locale (the locale prop, then the provider, then the page language): English, or Romanian for ro.
A checkout step that validates on submit and saves through the Server Action shown further down:
showSummary prints formatRoAddress under the form, the way the courier will read it: Str. Memorandumului 28, bl. A2, sc. 1, et. 3, ap. 12, 400114 Cluj-Napoca, jud. Cluj.
The parts a hand-rolled form usually misses:
- Phones. The county and locality lists open as bottom sheets below 768px, the postal code takes six digits with the numeric keypad, and Enter moves to the next part.
- Autofill. The county uses
address-level1, the localityaddress-level2, the streetaddress-line1and the codepostal-code, so a saved browser address fills the parts. - Paste. "Str. Memorandumului nr. 28, bl. A2" pasted into the street fills the street, number and block.
- County changes. Picking București swaps the locality for a sector select. Changing the county clears the locality and a postal code from another county, and keeps the street and number.
$ npx wingo-ui@latest add address-inputHow do I search localities without diacritics?
Fold both strings before you compare them: lowercase, decompose with NFD and drop the combining marks. "Iași".includes("iasi") is false, and folded, both sides read iasi. Ș and ț with a comma below only arrived in Unicode 3.0 in 1999, and older encodings had only the cedilla forms ş and ţ (opens in a new tab), which still turn up in imported data. Both decompose to a base letter plus a combining mark, so NFD folding treats Ștefănești, Ştefăneşti and stefanesti as one name.
text-match does that folding, ranks matches (exact, prefix, word prefix, inside a word) and returns the ranges to highlight, in about 200 lines with no dependencies. ro-locations builds its searchLocations on it, and Address Input's locality combobox filters with it.
$ npx wingo-ui@latest add ro-locationsFolding does not cover abbreviations or old spellings. People type "Tg. Mureș" for Târgu Mureș, and records from before the 1993 spelling rule write î inside words (Tîrgu). filterByText takes keywords next to the label, so aliases handle both:
Now "tg mures" and "tirgu mures" both find Târgu Mureș, ranked a little below a direct hit on the name.
How do I load every village from SIRUTA?
Serve the SIRUTA list from your own route and give Address Input a loadLocalities function that queries it. Without one, the component searches a built-in list of 332 cities, towns and large communes and accepts any other locality as typed, with a quiet note under the field. text-match has no "use client" directive, so the same search runs in a route handler:
The value you return becomes localityId, so the SIRUTA code is saved with the address.
How do I validate the address on the server?
Check it again in the Server Action, with a zod 4 schema (npm install zod) in a file that has no "use client". Copied component code has a trap here: address-input.tsx starts with "use client", so on the server every export is a client reference. Calling validateRoAddress or formatRoAddress in a Server Component or a Server Action throws "Attempted to call formatRoAddress() from the server but formatRoAddress is on the client". Type imports are fine, and ro-locations has no directive, so the schema can use its county lookup:
Errors from the action land on the matching part through the errors prop and win over the component's own. To format an address on the server for an order email, move formatRoAddress and its helpers into a module without the directive. You own the source.
What should I build next?
Try the postal code check in the demo above, then add the form to your checkout with npx wingo-ui@latest add address-input. Address Input and ro-locations are part of Wingo UI Pro ($8 a month, $80 a year or $150 once as of October 2026), and text-match is free. If the same checkout takes company details, Romanian CUI validation covers tax IDs and the IBAN validation guide covers bank accounts, while React form components explains the label, hint and error anatomy every part of this form shares. More step-by-step React builds live in the Build it category.
FAQ
How do you write an address in Romania?
Write the street type, name and number first (Str. Memorandumului 28), then the block, staircase, floor and apartment on the same line (bl. A2, sc. 1, et. 3, ap. 12). The next line holds the 6-digit postal code and the locality (400114 Cluj-Napoca), followed in domestic use by the county as jud. Cluj, and in Bucharest a Sector line goes above the postal code. Mail from abroad ends with ROMANIA on its own line.
How many digits does a Romanian postal code have?
Six. Six-digit codes replaced the four-digit ones on May 1, 2003. The first two digits identify the county, and in Bucharest they identify the sector, from 01 for Sector 1 to 06 for Sector 6.
What do bl., sc., et. and ap. mean in a Romanian address?
They abbreviate bloc (the apartment building), scară (the staircase, which has its own entrance), etaj (the floor) and apartament (the apartment). The ground floor is written parter.
How many counties does Romania have?
Romania has 41 counties (județe) plus the municipality of Bucharest, which is divided into six sectors. Their ISO 3166-2:RO codes, such as RO-CJ for Cluj and RO-B for Bucharest, use the same letters as Romanian license plates.
Should a Romanian address form require diacritics?
No. Accept what people type and match it without diacritics: the UPU's own sample addresses for Romania are written without them, and older data uses the cedilla forms ş and ţ. Store the official spelling from your locality list once the person picks a match.
Are Address Input and the Romanian locations data free?
Address Input and ro-locations are part of Wingo UI Pro. text-match, the diacritic-insensitive search both of them use, is free and installs with npx wingo-ui@latest add text-match.
- Romania
- Address Form
- React
- Postal Codes
- Forms
- Next.js