Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Forms

Address Input

Forms

Address Input React component for Tailwind CSS v4

Tree SelectBooking Widget
Live props
Appearance
variant
size
fieldVariant
radius
Layout
layout
collapseApartment
Behavior
validateOn
name
responsive
locale
Content
showSummary
countryLabel
label
description
State
error
required
invalid
disabled
readOnly
Code
import { AddressInput } from "@/components/ui/address-input"

<AddressInput />
  1. Home
  2. Components
  3. Forms
  4. Address Input
Pro

Address Input React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React address input for Romanian addresses: county, locality search, street, number, block and apartment, and a checked postal code.

Pick the county from a searchable list (type to filter, arrows and Enter; a bottom sheet on phones), then type the locality: the cities of that county show as you type, diacritics do not matter ("cluj", "iasi"), Enter picks the highlighted one, and a village that is not in the list is kept as typed through the "Use …" row, with a quiet note under the field (never an error) that it is not in the list and to check the spelling. Picking a city before the county fills the county too. Choosing București swaps the locality for a Sector select; changing the county clears the locality and a postal code from another county but keeps the street and the number. Street and number are side by side from 512px of width; "Add block, entrance, apartment" opens the bl. / sc. / et. / ap. row on a smooth collapse and moves focus into it (it is open from the start when the value has any of them). The postal code takes digits only, six at most, with the numeric keypad on phones; leaving it with a code from another county shows an amber warning with an icon under it, never an error. Enter in a part moves to the next one and submits only from the last; the phone keyboard says Next and then Done. Required parts show their message on blur (or on change, or only after the form is submitted); server errors land on their field, and a county code the list does not know (old data such as "BUC") is flagged at once. Browser autofill works on every part, and "Str. Memorandumului nr. 28, bl. A2" pasted or autofilled into the street is split into the parts; a full address ("…, sat Feleacu, jud. Cluj, 407280") also fills the county, locality, sector and postal code, but only the ones that are still empty, and never a locality from another county than the chosen one. The single-line variant is one search field: type an address, the suggestions (offline parsing and the localities list, or your searchAddress) fill every part and open the details below for a final check; leaving the field with text that was not picked (Tab or a tap outside) parses that text into the parts. Read-only shows the formatted address with a copy button; reduced motion turns the collapses into fades.

Address Input is a Pro React component in the React form components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add address-input, together with Button, Collapsible, Combobox, Field, Input and 7 more and the npm packages motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add address-input

Address Input is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import AddressInput from @/components/ui/address-input.

import { useState } from "react"
import { AddressInput, EMPTY_RO_ADDRESS, type RoAddress } from "@/components/ui/address-input"

export function ShippingAddress() {
  const [address, setAddress] = useState<RoAddress>(EMPTY_RO_ADDRESS)
  const [valid, setValid] = useState(false)
  return (
    <form action="/api/checkout" className="flex flex-col gap-4">
      <AddressInput
        label="Delivery address"
        name="shipping"
        value={address}
        onValueChange={(next, validation) => {
          setAddress(next)
          setValid(validation.valid)
        }}
        showSummary
      />
      <button type="submit" disabled={!valid}>Continue</button>
    </form>
  )
}

Props

  • value
    RoAddress

    The controlled address: { county (plate code "CJ", "B"), locality, localityId?, sector? (București), street, number, building?, entrance?, floor?, apartment?, postalCode?, details? }.

  • defaultValuedefault "EMPTY_RO_ADDRESS"
    RoAddress

    The starting address when uncontrolled.

  • onValueChange
    (address: RoAddress, validation: { valid: boolean; errors: AddressErrors; warnings: AddressErrors }) => void

    Every change with the whole address and its validity, so a submit button can follow valid.

  • variantdefault "form"
    "form" | "single"

    form: the parts. single: one autocomplete field that fills the parts, with the parts under an "Edit details" collapsible.

  • layoutdefault "auto"
    "auto" | "stacked"

    auto: one column until the component is 512px wide, then 6 columns (county and locality 3 + 3, street 4 and number 2, postal code 2 and details 4). stacked: one column always.

  • fieldsdefault "all"
    AddressField[]

    Which parts render: a courier form hides details, a billing form hides the apartment row.

  • requiredFieldsdefault "["county", "locality", "street", "number"]"
    AddressField[]

    Marked with * and validated. In București the sector is required in place of the locality.

  • collapseApartmentdefault true
    boolean

    The block / entrance / floor / apartment row waits behind a link until used; it is open when the value has any of them.

  • validateOndefault "blur"
    "blur" | "change" | "submit"

    When a part's error shows. A submit of the enclosing form shows them all.

  • errors
    AddressErrors

    Server errors per field, e.g. { street: "This street is not in the official street list" }. They win over the computed ones.

  • showSummarydefault false
    boolean

    The formatted one-line address under the form, crossfading as it changes.

  • countryLabeldefault ""
    ReactNode

    A read-only country row at the top ("Romania"), for forms that print the country.

  • loadLocalities
    (county: string, query: string, context: { signal: AbortSignal }) => Promise<{ value: string; label: string; description?: string }[]>

    Your full nomenclature (SIRUTA). value becomes localityId. Without it the cities of the county come from ro-locations plus a built-in list of every Romanian town (Pașcani, Onești, Moinești...), and free text is accepted for villages.

  • searchAddress
    (query: string, context: { signal: AbortSignal }) => Promise<AddressSuggestion[]>

    The single variant's online suggestions ({ id, label, description?, address }), debounced 400ms. Without it the text is parsed offline.

  • labeldefault ""
    ReactNode

    The group's legend (a fieldset); every part has its own label too. The demo shows "Delivery address" while it is empty.

  • descriptiondefault ""
    ReactNode

    Helper text under the legend.

  • errordefault ""
    ReactNode

    A message for the whole group, linked to the fieldset.

  • requireddefault false
    boolean

    The * after the legend. Which parts are required is requiredFields.

  • invaliddefault false
    boolean

    Every part in the invalid look without a message.

  • disableddefault false
    boolean

    Dims and blocks every part (a disabled fieldset).

  • readOnlydefault false
    boolean

    The formatted address as text on several lines with a copy button; "-" when empty.

  • sizedefault "md"
    "sm" | "md" | "lg"

    Every part 32 / 40 / 48px (md 44px on touch), with matching label and text sizes.

  • fieldVariantdefault "soft"
    "soft" | "outline" | "underline" | "ghost"

    The box of every part (variant is the form / single choice here).

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    Corners of every box, scaled per size like the button's.

  • namedefault ""
    string

    Adds hidden inputs and names for native forms: `${name}.county`, `${name}.locality`, `${name}.localityId`, `${name}.street`… The demo lists them in the readout.

  • responsivedefault true
    boolean

    The county, sector and locality lists open as bottom sheets below 768px (the locality sheet has its own search box).

  • localedefault ""
    string

    A ro locale picks ADDRESS_INPUT_LABELS_RO on its own. Empty uses the UIProvider locale, then the browser's. The demo defaults to en-US.

  • labels
    Partial<AddressInputLabels>

    Every string: the part labels (county, locality, sector, street, number, building, entrance, floor, apartment, postalCode, details, country), more, search, editDetails, required, postalInvalid, postalMismatch(county), localityCustom(text), localityUnlisted(text), noLocalities, placeholders, countyUnknown, noResults, loading, copy, copied, filled, clear, cancel, showSuggestions. English by default; ADDRESS_INPUT_LABELS_RO is the ready Romanian set.

  • fieldProps
    Partial<FieldGroupProps>

    More for the fieldset: variant "card", classNames.

  • id
    string

    The prefix of every part's id (`${id}-street`).

  • classNames
    Partial<Record<"grid" | "country" | "county" | "locality" | "sector" | "street" | "number" | "more" | "apartmentRow" | "postal" | "details" | "warning" | "single" | "editDetails" | "summary" | "readOnly", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the root inside the fieldset.

Parts

  • formatRoAddress

    function

    "Str. Memorandumului 28, bl. A2, sc. 1, et. 3, ap. 12, 400114 Cluj-Napoca, jud. Cluj"; București ends with "Sector 3, 030167 București". Signature: (address, { multiline = false, includeCounty = true, includeCountry = false }) => string.

  • validateRoAddress

    function

    The component's validation, for a form's own submit handler. A postal code from another county is a warning. Signature: (address, { requiredFields?, fields?, labels? }) => { valid; errors; warnings }.

  • parseRoAddress

    function

    Best effort: street type and name, nr. or a trailing number, bl. / sc. / et. / ap., a 6-digit postal code, sector, a known locality and county. Signature: (text: string) => Partial<RoAddress>.

  • postalCodeMatchesCounty

    function

    false only for a complete code whose first two digits belong to another county (RO_POSTAL_PREFIXES). Signature: (code: string, county: string, sector?: string) => boolean.

  • RO_POSTAL_PREFIXES

    constant

    The two-digit postal code prefixes per county (București 01 to 06), used by postalCodeMatchesCounty. Type: Record<string, string[]>.

  • RO_STREET_TYPES

    constant

    The street types the street field offers and the parser recognises ("Str.", "Bd.", "Calea"…). Type: readonly string[].

  • EMPTY_RO_ADDRESS

    constant

    The empty address, a starting value for a controlled form. Type: RoAddress.

  • DEFAULT_ADDRESS_INPUT_LABELS

    constant

    The English default labels. Type: AddressInputLabels.

  • ADDRESS_INPUT_LABELS_RO

    constant

    The ready Romanian label set (Județ, Localitate, Bloc, Scara, Etaj, Ap.). Type: AddressInputLabels.

  • RoAddress

    type

    The value: county (the plate code, "B" for Bucharest), locality, localityId, sector (Bucharest only), street, number, building, entrance, floor, apartment, postalCode and details.

  • AddressField

    type

    A key of RoAddress, used by fields, requiredFields and errors.

  • AddressErrors

    type

    Messages per part, Partial<Record<AddressField, string>>, for server errors through errors.

  • AddressSuggestion

    type

    One hit of searchAddress: id, label, description and a partial RoAddress that fills the parts.

Examples

București

Choosing București swaps the locality for a sector select; the summary ends in "Sector 3, 030167 București".

A village not in the list

Localities outside the offline list are typed freely; 407280 matches Cluj, so no warning.

Postal code warning

400114 is a Cluj code in county Iași: a warning with an icon, never a blocking error.

Single-line variant

One field with offline parsing; a suggestion fills the parts and opens them for bl. / ap.

Billing address

No apartment row and no details, a read-only country row and outline boxes.

Validation on submit

Errors wait for the submit; the server's answer lands on the street field.

Read-only

The formatted address as text with a copy button, for order details.

Dependencies

motionlucide-reactbuttoncollapsiblecomboboxfieldinputselectuse-controllable-stateuse-localemotionro-locationstext-matchui-config

Files

  • components/ui/address-input.tsx

FAQ

Is the React Address Input component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Address Input?

Run npx wingo-ui@latest add address-input. It also adds the Wingo UI items it builds on (Button, Collapsible, Combobox, Field, Input and 7 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/address-input.

How does Address Input behave on phones?

The county, sector and locality lists open as bottom sheets below 768px (the locality sheet has its own search box). The responsive prop controls this and is on by default.

See also

SelectComboboxRomanian locationslibLocation MapField

Used in

Post Listing WizardSettings Page

More React form components

Booking WidgetCSV ImportForm WizardInline EditInvoice line itemsRich Text Editor
All 9 React form components

Articles using this component

  • Romanian Address Format: The Rules and a React Form

    Build it: screens and apps · Oct 9, 2026 · 10 min read

PreviousTree SelectNext Booking Widget

Delivery for Emma Carter

Order SM-2026-0924 · Summit Market · 2 items

Delivery address
Cluj-Napoca46.7712° N, 23.6236° E

Cluj-Napoca · 46.7712° N, 23.6236° E

valid: true · validateOn: blur · responsive: true · collapseApartment: true
Str. Memorandumului 28, bl. A2, sc. 1, et. 3, ap. 12, 400114 Cluj-Napoca, jud. Cluj