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

Inputs

Input Group

Inputs

Input Group React component for Tailwind CSS v4

InputListbox
Live props
Field
label
description
error
required
invalid
disabled
readOnly
Appearance
size
variant
radius
separators
Code
import { InputGroup } from "@/components/ui/input-group"

<InputGroup />
  1. Home
  2. Components
  3. Inputs
  4. Input Group
Pro

Input Group React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React input group that joins a field with text addons, selects, icons and buttons into one box with one focus ring and one error state.

Several controls read as one field: the whole row takes a single soft ring when any part has focus, a single red ring when it is invalid, and dims as one when disabled. Tab walks through the controls in order. A click or tap on a text addon like https:// or kg lands the caret in the input next to it; a click on the label focuses the first control. Selects inside open their list as usual, as a bottom sheet below 768px. A solid button at the end (Apply) sits flush with the rounded corner and shows its spinner while loading without changing width. On a phone the row stays on one line down to 320px: addons never wrap, the input shrinks, text is 16px so iOS never zooms and the md row grows to 44px.

Input Group is a Pro React component in the React input components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add input-group, together with Field, Button and UIProvider and the npm package @radix-ui/react-slot.

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 input-group

Input Group 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 InputGroup from @/components/ui/input-group.

import { useState } from "react"
import { Input } from "@/components/ui/input"
import { InputGroup, InputGroupAddon, InputGroupButton } from "@/components/ui/input-group"
import { Select } from "@/components/ui/select"

const DOMAINS = [
  { value: ".com", label: ".com" },
  { value: ".io", label: ".io" },
  { value: ".dev", label: ".dev" },
]

export function SiteAddress() {
  const [domain, setDomain] = useState(".com")
  const [pending, setPending] = useState(false)

  return (
    <div className="flex flex-col gap-4">
      <InputGroup label="Website address" description="The name without spaces.">
        <InputGroupAddon>https://</InputGroupAddon>
        <Input aria-label="Domain" placeholder="northwind" name="domain" />
        <Select aria-label="Extension" options={DOMAINS} value={domain} onValueChange={(next) => next && setDomain(next)} />
      </InputGroup>

      <InputGroup label="Discount code" error="This coupon has expired">
        <Input aria-label="Coupon code" placeholder="Coupon code" />
        <InputGroupButton
          variant="solid"
          loading={pending}
          onClick={() => {
            setPending(true)
            setTimeout(() => setPending(false), 1200)
          }}
        >
          Apply
        </InputGroupButton>
      </InputGroup>
    </div>
  )
}

Props

  • labeldefault ""
    ReactNode

    Names the whole row (the group is aria-labelledby it); a click on it focuses the first control. The controls inside stay bare and take an aria-label each (Domeniu, Extensie).

  • descriptiondefault ""
    ReactNode

    Helper text under the row. It describes the group and every control inside (aria-describedby), together with the text addons.

  • errordefault ""
    ReactNode

    Replaces the description with the animated error and turns the whole row red; every control inside gets aria-invalid.

  • requireddefault false
    boolean

    The red mark after the label; the controls inside get required.

  • invaliddefault false
    boolean

    The red ring and aria-invalid on every control without a message.

  • disableddefault false
    boolean

    Dims the row once to 50% and disables every control and button inside.

  • readOnlydefault false
    boolean

    Values stay visible and selectable but cannot change; selects do not open and the row drops its hover tint. The demo fills in a sample value.

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

    Height 32 / 40 / 48px (md 44px on touch). Pass it to the group only: the controls, addons and buttons inside follow it.

  • variantdefault "soft"
    "soft" | "outline"

    soft is the filled well, outline a bordered box. Ghost and underline make no sense for a joined row.

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

    Corner shape of the row, scaled with the size like the button's (md is 8 / 10 / 12px); full makes a pill. Inner parts are clipped to it.

  • separatorsdefault true
    boolean

    Hairlines between segments (controls, filled addons, ghost buttons). Plain addons are text inside a segment and a solid button is its own edge, so they never get one.

  • children
    ReactNode

    InputGroupAddon, bare W2 controls (Input, Select, NumberInput, CurrencyInput... with no label of their own, an aria-label each) and InputGroupButton, in order. The row never wraps (the input shrinks instead); on a phone keep it to one or two controls plus addons, and split longer rows into separate groups.

  • classNames
    Partial<Record<"field" | "label" | "description" | "error" | "box" | "separator", string>>

    Extra classes for the parts: field is the wrapper with the label, box the joined row.

  • className
    string

    Classes for the outermost element: the wrapper when there is a label, description or error, else the row.

Parts

  • InputGroupAddon

    component

    Text or an icon at either end. position is inferred from the order when omitted. variant "plain" (default) is muted text that hugs the input, "filled" a tinted segment like a classic addon. A click or tap focuses the nearest input (links and buttons inside keep their own click). asChild renders a <label htmlFor>. Text addons (USD, kg) join the controls' aria-describedby; describe={false} opts out. Props: ComponentProps<"span"> & { position?: "start" | "end"; variant?: "plain" | "filled"; asChild?: boolean; describe?: boolean }.

  • InputGroupButton

    component

    A Button flush with the row: variant "ghost" and radius "none" by default, full height, the group's size (an explicit sm / md / lg sets its own text size), inset focus outline, disabled with the group. size="icon" makes it square; loading keeps its width. Props: ButtonProps.

  • InputGroupSeparator

    component

    A 1px hairline placed by hand, for example with separators={false}. Decorative (aria-hidden). Takes span props.

Examples

Price

A CurrencyInput joined with a currency Select: one box, one ring, and the amount reformats for USD or EUR.

Coupon

A solid Apply button flush with the end: it checks the code for 1.2s, then SUMMER24 shows the error in red and FALL25 applies the discount.

Weight and size

A plain kg addon that reads as text inside the box, and a filled cm segment like a classic addon.

Search with category

A pill search bar: category Select, the query and an icon button that runs a pretend search. Below 768px the category opens as a bottom sheet.

Country code

A filled +1 segment before a tel input. For real phone numbers use phone-input.

Sizes and variants

sm, md and lg in soft and outline, lined up with a plain Input under each set.

Dependencies

@radix-ui/react-slotfieldbuttonui-config

Files

  • components/ui/input-group.tsx

FAQ

Is the React Input Group component free?

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

How do I install Input Group?

Run npx wingo-ui@latest add input-group. It also adds the Wingo UI items it builds on (Field, Button and UIProvider) and the npm package @radix-ui/react-slot. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/input-group.

How does Input Group behave on phones?

Selects inside open their list as usual, as a bottom sheet below 768px. On a phone the row stays on one line down to 320px: addons never wrap, the input shrinks, text is 16px so iOS never zooms and the md row grows to 44px.

See also

InputFieldButton GroupSelectCurrency Input

Used in

Settings Page

More React input components

ListboxMention InputMonth PickerMulti SelectNumber InputPassword Input
All 40 React input components
PreviousInputNext Listbox
Website address
https://
Your site: https://northwind.com