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

Inputs

Input React component for Tailwind CSS v4

Filter BuilderInput Group
Live props
Field
label
description
error
required
invalid
disabled
readOnly
Content
placeholder
defaultValue
prefix
suffix
clearLabel
loadingLabel
name
Appearance
size
variant
radius
accent
floatingLabel
State
loading
Behavior
type
clearable
showCount
maxLengthNo limit
selectOnFocus
Code
import { Input } from "@/components/ui/input"

<Input />
  1. Home
  2. Components
  3. Inputs
  4. Input
Free

Input React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React text input: icons and addons inside the box, a clear button, a loading spinner, a character counter and floating labels.

Click anywhere in the box, on an icon, on the padding or on a suffix like USD, and the caret lands in the text without jumping; the whole box takes a soft ring. Type and a clear button pops in at the end: tap it or press Escape and the text is gone while focus stays in the field. While loading a small spinner spins where the trailing icon was, and the field stays editable. With a floating label the label rests inside the box like a placeholder and glides up to the top edge on focus, when filled or when the browser autofills. With a counter the numbers turn amber at 90% and red at the limit. Enter submits the form like a native field. On a phone the text is 16px so iOS never zooms, the keypad follows the type (email, url, tel, decimal for amounts), the md box grows to 44px and the clear button has a 44px hit area.

Input is a free 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, together with UIProvider, Field, Spinner, Motion presets and useControllableState 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 input

Usage

Import Input from @/components/ui/input.

import { useRef, useState } from "react"
import { CircleCheck, MapPin } from "lucide-react"
import { Input } from "@/components/ui/input"

export function ListingDetails() {
  const [title, setTitle] = useState("")
  const [user, setUser] = useState("emma.carter")
  const [checking, setChecking] = useState(false)
  const timer = useRef<ReturnType<typeof setTimeout>>(undefined)

  return (
    <form className="flex flex-col gap-4">
      <Input
        name="title"
        label="Listing title"
        placeholder="e.g. Trek FX 2 city bike"
        value={title}
        onValueChange={setTitle}
        error={title && title.length < 10 ? "The title needs at least 10 characters." : undefined}
        showCount
        maxLength={70}
        required
        enterKeyHint="next"
      />
      <Input name="price" label="Price" prefix="$" inputMode="decimal" selectOnFocus defaultValue="1,250" />
      <Input name="city" label="City" leftIcon={<MapPin />} clearable autoComplete="address-level2" />
      <Input
        label="Username"
        value={user}
        onValueChange={(next) => {
          setUser(next)
          // the check runs 1.4s after the last keystroke
          setChecking(true)
          clearTimeout(timer.current)
          timer.current = setTimeout(() => setChecking(false), 1400)
        }}
        loading={checking}
        rightIcon={<CircleCheck className="text-success-soft-foreground" />}
        autoCapitalize="none"
        enterKeyHint="done"
      />
    </form>
  )
}

Props

  • labeldefault ""
    ReactNode

    Renders the field anatomy around the box: the label above it, linked with htmlFor. With floatingLabel it moves inside the box. Leave it empty for a bare input (then pass aria-label).

  • descriptiondefault ""
    ReactNode

    Helper text under the box, linked with aria-describedby. The error takes its place while it shows.

  • errordefault ""
    ReactNode

    Replaces the description with an alert icon and the text, slides in and marks the field invalid (red ring, aria-invalid). Empty means no error.

  • requireddefault false
    boolean

    The red mark after the label (hidden from screen readers) and the native required attribute.

  • invaliddefault false
    boolean

    The invalid look and aria-invalid without a message, for forms that show the error somewhere else.

  • disableddefault false
    boolean

    Dims the field to 50% and blocks input; the native input is disabled, so the form does not submit it.

  • readOnlydefault false
    boolean

    The value stays visible, selectable and submitted, but cannot change; the box drops its hover tint and the clear button. The demo fills in a sample value.

  • placeholderdefault ""
    string

    Hint text while the field is empty. With a floating label it only shows while the label is floated (on focus, or always next to a prefix), never behind it.

  • defaultValuedefault ""
    string

    The starting text while uncontrolled. Pass value and onValueChange instead to control it; a value set by a form library straight on the element shows up in the counter after the next focus.

  • prefixdefault ""
    ReactNode

    Text before the input, like "https://" or "+40", in the muted addon look (long text truncates at 40% of the box), or an element rendered as is. A click on it lands in the input.

  • suffixdefault ""
    ReactNode

    Text after the input, like "USD" or "@company.com", or an element such as a small Apply button. Buttons inside keep the input focused while pressed.

  • clearLabeldefault "Clear"
    string

    The clear button's accessible name and hover title. The demo passes Clear text (the City field always shows one).

  • loadingLabeldefault "Loading"
    string

    What screen readers hear for the spinner while loading. The demo passes Checking.

  • namedefault ""
    string

    The form field name. It lands on the native input, so FormData, autofill and {...register('titlu')} from react-hook-form work as on a plain input.

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

    Height 32 / 40 / 48px with 13 / 14 / 15px text (16px on phones so iOS never zooms); md grows to 44px on touch. Floating labels make the box 44 / 52 / 60px.

  • variantdefault "soft"
    "soft" | "outline" | "underline" | "ghost"

    soft is a filled well that works on every surface, outline a bordered box, underline a bottom line that grows a bar from the center on focus, ghost has no fill until hover (toolbars, table cells).

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

    Corner shape, scaled with the size like the button's (md is 8 / 10 / 12px); full makes a pill. Ignored by underline.

  • accentdefault ""
    string

    Any CSS color for the focus ring, the outline border and underline bar on focus, and the caret. Empty uses the ring token (the brand orange).

  • floatingLabeldefault false
    boolean

    The label sits inside the box like a placeholder and floats up, at 80%, on focus, when filled or when autofilled (CSS only, so it is right before hydration). The box grows to 44 / 52 / 60px for the room. Needs label; a prefix keeps it floated.

  • loadingdefault false
    boolean

    A small spinner at the end, in place of the right icon when there is one, and aria-busy. The input stays editable, e.g. while checking if a username is free.

  • typedefault "text"
    InputType: "text" | "email" | "url" | "tel" | "search" | "password" | "number"

    The native type, which also picks the phone keypad (email, url, tel, search). number renders a text field with a decimal keypad, so maxLength works and a scroll never changes it; number-input adds steppers and locale parsing, password-input the reveal button.

  • clearabledefault false
    boolean

    A clear button pops in while there is a value (never when disabled or read-only). It stays out of the Tab order; Escape clears from the keyboard. Focus stays in the input.

  • showCountdefault false
    boolean

    The "12 / 70" counter at the end of the message row, amber from 90% and red at the limit. Needs maxLength.

  • maxLengthdefault 0
    number

    The native character limit, also the counter's maximum. 0 means no limit.

  • selectOnFocusdefault false
    boolean

    Selects the whole text when the field takes focus, by click, tap or Tab, so typing replaces it. For codes and amounts.

  • value
    string

    The text, controlled. Without onValueChange typing does nothing (like React); use readOnly for a fixed value.

  • onValueChange
    (value: string) => void

    Fires with the new text on every edit, including a clear.

  • onChange
    (event: ChangeEvent<HTMLInputElement>) => void

    The native change event, untouched. A clear fires it too, like a keystroke.

  • onClear
    () => void

    Called after the clear button or Escape emptied the field. Focus is already back in the input.

  • leftIcon
    ReactNode

    A leading icon in the muted color; unsized svgs follow the size (14 / 16 / 18px). A click on it lands in the input.

  • rightIcon
    ReactNode

    A trailing icon, e.g. a green check after a successful check. It pops in and out, and crossfades with the spinner while loading.

  • clearIcon
    ReactNode

    Replaces the clear button's icon (a 16px X by default).

  • fieldProps
    Partial<FieldProps>

    Anything else for the Field wrapper: optional and optionalLabel, labelAction, orientation, labelWidth, hideLabel, counterLabel, requiredIndicator.

  • id
    string

    The native input's id. Defaults to useId(); inside a <Field> the Field's id wins.

  • ref
    RefObject<HTMLInputElement | null> or (node) => void

    The native input. Every other native prop lands there too: inputMode (defaults from the type), enterKeyHint, autoComplete, pattern, spellCheck, autoCapitalize, onBlur, onKeyDown, aria-* and data-*.

  • classNames
    Partial<Record<"field" | "label" | "description" | "error" | "counter" | "box" | "input" | "prefix" | "suffix" | "leftIcon" | "rightIcon" | "clear" | "spinner" | "floatingLabel", string>>

    Extra classes for the inner parts. box targets the field box, input the native element.

  • className
    string

    Classes for the outermost element: the Field root when there is a label, description, error or counter, else the box.

Examples

Icons and addons

A leading icon, text addons on both sides, and a small Apply button as the suffix: it checks the code for a second, keeps the input focused and swaps for the discount.

Variants and sizes

soft, outline, underline and ghost at 32, 40 and 48px, with icons, a clear button and a suffix.

Loading

An availability check 1.4s after typing stops: the check icon crossfades into a spinner and back; try emma for a taken name or a capital letter for the format error.

Clearable search

The clear button pops in with the first letter; Escape clears from the keyboard and focus never leaves the field. Every word matches on its own, accents folded; the label is kept for screen readers only (fieldProps.hideLabel).

Floating labels

An outline checkout form: labels rest inside the box and float up on focus or when filled; a prefix keeps the label up. Leave the email field to see its error.

Counter

showCount with maxLength 70: amber from 63 characters, red at the limit. Add the suggestions to walk it there.

States

Read-only (selectable, selected on focus), disabled with a prefix, and invalid without a message.

Accent and pill

radius full with a blue accent: focus the field and the ring and caret match the button next to it.

Dependencies

motionlucide-reactui-configfieldspinnermotionuse-controllable-state

Files

  • components/ui/input.tsx

FAQ

Is the React Input component free?

Yes. The source is free and needs no account: copy it from the Code tab or install it with the CLI.

How do I install Input?

Run npx wingo-ui@latest add input. It also adds the Wingo UI items it builds on (UIProvider, Field, Spinner, Motion presets and useControllableState) and the npm packages motion and lucide-react. With shadcn, run npx shadcn@latest add https://wingo-ui.com/r/input.json.

How does Input behave on phones?

On a phone the text is 16px so iOS never zooms, the keypad follows the type (email, url, tel, decimal for amounts), the md box grows to 44px and the clear button has a 44px hit area.

See also

FieldPassword InputNumber InputSearch InputMasked InputTextarea

Used in

Onboarding FlowLogin PageFile managerMasked InputWorkflow CanvasCheckout stepsData TableCategory pickerFilter panelCron PickerFilter BuilderPassword Input

and 52 more.

More React input components

Input GroupListboxMention InputMonth PickerMulti SelectPhone Input
All 40 React input components

Articles using this component

  • Shadcn Number Input: Steppers, Min, Max and Decimals

    Component guides · Nov 6, 2026 · 8 min read

  • Nextjs Contact Form That Sends Email and Blocks Spam

    Build it: screens and apps · Nov 5, 2026 · 10 min read

  • React Settings Page: Section Nav, Save Bar, Danger Zone

    Build it: screens and apps · Oct 16, 2026 · 11 min read

  • Claude Code shadcn Setup: MCP Server, Skill and CLAUDE.md

    Building UI with AI agents · Oct 9, 2026 · 12 min read

PreviousFilter BuilderNext Input Group

Post a listing

About the item

Step 1 of 3
$
At least 10 characters in the title

Press Continue with a short title to see the error