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

Inline Edit

Forms

Inline Edit React component for Tailwind CSS v4

Form WizardInvoice line items
Live props
Content
type
defaultValue
emptyText
placeholder
aria-label
Appearance
size
variant
radius
showIcon
Layout
fullWidth
State
defaultEditing
disabled
readOnly
Behavior
activation
showActions
blurAction
selectOnFocus
maxLengthNo limit
successDuration1200ms
locale
Code
import { InlineEdit } from "@/components/ui/inline-edit"

<InlineEdit />
  1. Home
  2. Components
  3. Forms
  4. Inline Edit
Pro

Inline Edit React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React inline edit field for text, numbers and selects that edits in place and saves optimistically.

Click or tap the value and it becomes an editor of the same size, text selected, with the text in exactly the same place. Enter saves, Escape cancels and puts focus back on the value, Tab or a click elsewhere saves (blurAction). A textarea makes new lines with Enter and saves with Cmd+Enter (Ctrl+Enter on Windows); a select opens at once and saves on the pick, as a bottom sheet on phones. From the keyboard, Tab to the value and press Enter, Space or F2. The new value shows at once with a small spinner, a check pops in when the save lands, and a failed save rolls back, shakes the value and shows the error. On touch the pencil is always visible, the save and cancel buttons get 44px hit areas and the keyboard shows Done.

Inline Edit 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 inline-edit, together with UIProvider, Input, Textarea, Number Input, Select 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 inline-edit

Inline Edit 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 InlineEdit from @/components/ui/inline-edit.

import { InlineEdit } from "@/components/ui/inline-edit"

const STATUSES = [
  { value: "active", label: "Active" },
  { value: "reserved", label: "Reserved" },
  { value: "sold", label: "Sold" },
]

export function ListingDetails({ save }: { save: (field: string, value: unknown) => Promise<void> }) {
  return (
    <dl className="grid grid-cols-[96px_1fr] items-center gap-x-4 gap-y-1">
      <dt className="text-sm text-muted-foreground">Title</dt>
      <dd>
        <InlineEdit
          aria-label="Listing title"
          defaultValue="Trek FX 2 city bike"
          emptyText="Add a title"
          maxLength={70}
          validate={(v) => (v.trim() ? null : "The title cannot be empty")}
          onSave={(v) => save("title", v)}
        />
      </dd>
      <dt className="text-sm text-muted-foreground">Price</dt>
      <dd>
        <InlineEdit
          type="number"
          aria-label="Price"
          defaultValue={280}
          locale="en-US"
          format={{ style: "currency", currency: "USD", maximumFractionDigits: 0 }}
          onSave={(v) => save("price", v)}
        />
      </dd>
      <dt className="text-sm text-muted-foreground">Status</dt>
      <dd>
        <InlineEdit type="select" aria-label="Status" options={STATUSES} defaultValue="active" onSave={(v) => save("status", v)} />
      </dd>
    </dl>
  )
}

Props

  • typedefault "text"
    "text" | "textarea" | "number" | "select"

    The editor: a single-line Input, a growing Textarea, a NumberInput (value is number | null, formatted with locale and format) or a Select (needs options, saves on the pick).

  • value
    string | number | null

    Controlled value: a string, or number | null for type="number". Pair it with onValueChange, which fires when a save starts (optimistic) and again with the old value if it fails.

  • defaultValuedefault ""
    string | number | null

    Starting value while uncontrolled: "" for text, null for numbers. The playground reads a number for type="number" and an option value (active, reserved, sold) for select.

  • onValueChange
    (value) => void

    Called with the new value when a save starts, and with the previous one when the save fails (rollback).

  • onSave
    (value) => (void | PromiseLike<unknown>)

    Persists the value. A promise keeps the saving spinner; a rejection rolls back, shakes the value and shows the error (its message, else labels.failed). Unchanged values skip it; a save made while one is pending waits and only the latest result counts.

  • validate
    (value) => (string | null | undefined)

    Return a message to keep the editor open with the error under it. It re-checks while typing once it shows.

  • options
    SelectItems

    The options of a select editor: { value, label, description?, icon? } or groups of them.

  • emptyTextdefault "Click to add"
    string

    The muted display while the value is empty.

  • placeholderdefault ""
    string

    The editor's placeholder, shown while the draft is empty.

  • aria-labeldefault ""
    string

    Names the display button and the editor. Screen readers hear "Listing title: Trek FX 2 city bike, Edit".

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

    Text 13 / 14 / 15px (16px on phones) and the editor's field size (32 / 40 / 48px, md 44px on touch). A heading look comes from className (text-2xl font-semibold); the editor follows it.

  • variantdefault "ghost"
    "ghost" | "underline"

    The display: ghost tints on hover like a quiet field, underline draws a dashed line under the text (for values inside a sentence).

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

    Corner shape of the hover tint and the editor box, scaled per size like the button's (sm is 6 / 8 / 10px).

  • showIcondefault true
    boolean

    The pencil after the value: on hover and keyboard focus with a mouse, always visible on touch.

  • icon
    ReactNode

    Replaces the pencil (and the icon of the edit button with activation="button").

  • fullWidthdefault true
    boolean

    Fills its container. false sizes it to the text, so it sits inside a sentence (wrap the sentence in a div, not a <p>: the editor holds block elements); the editor opens as wide as the text was.

  • defaultEditingdefault false
    boolean

    Starts in edit mode (without taking focus). Use editing / onEditingChange to control it.

  • editing
    boolean

    Controlled edit mode.

  • onEditingChange
    (editing: boolean) => void

    Called when editing starts and ends.

  • onCancel
    () => void

    Called after Escape, the cancel button, a select closed without a pick, or a blur with blurAction="cancel".

  • disableddefault false
    boolean

    Dims the value and blocks editing.

  • readOnlydefault false
    boolean

    Shows the value only: not a button, no pencil, text selectable.

  • activationdefault "click"
    "click" | "doubleclick" | "button"

    click: a click or tap edits. doubleclick: a double click edits (a single tap on touch, Enter from the keyboard), for headings people select often. button: only the pencil button edits, so links in the value stay clickable.

  • showActionsdefault true
    boolean

    Save and cancel buttons after the editor (below a textarea, with the Cmd+Enter hint). Selects save on the pick and never show them.

  • blurActiondefault "save"
    "save" | "cancel" | "none"

    What leaving the editor (Tab, a click elsewhere) does. none keeps the editor open until Enter, Escape or a button.

  • selectOnFocusdefault true
    boolean

    Selects the text when the editor opens, so typing replaces it. A textarea puts the caret at the end instead.

  • maxLengthdefault 0
    number

    The editor's character limit (text and textarea); 0 = none.

  • successDurationdefault 1200
    number

    Milliseconds the saved check stays after a save lands; 0 = no check.

  • localedefault ""
    string

    Formats and parses numbers ("1,200.5" in en-US, "1.200,5" in de-DE). Defaults to the nearest LocaleProvider, then the browser.

  • format
    Intl.NumberFormatOptions

    Number display: { style: "currency", currency: "USD", currencyDisplay: "narrowSymbol" }, units, percent.

  • renderValue
    (value) => ReactNode

    Custom display, e.g. a price with a badge or a link (pair it with activation="button").

  • labels
    { edit?; save?; cancel?; saving?; saved?; failed?; saveHint? }

    Every string: { edit: "Edit", save: "Save", cancel: "Cancel", saving: "Saving", saved: "Saved", failed: "Could not save", saveHint: "to save" }. Saved and failed are announced in a polite live region.

  • editorProps
    Partial<InputProps | TextareaProps | NumberInputProps | SelectProps>

    Extra props for the editor: min / max / step / steppers for numbers, maxRows for a textarea, search for a select.

  • classNames
    Partial<Record<"display" | "value" | "empty" | "icon" | "status" | "editor" | "actions" | "save" | "cancel" | "hint" | "error", string>>

    Extra classes for the inner parts.

  • className
    string

    Classes for the root, merged last. Font classes here (text-2xl font-semibold) style both the value and the editor.

Parts

  • InlineEditValue

    type

    The value type: string for text, textarea and select; number | null for type="number".

  • InlineEditLabels

    type

    The labels object: edit, save, cancel, saving, saved, failed and saveHint.

Examples

Editable price cells

A table where there is room and cards on phones (a container query), both controlled by the same state: edit a price, the total follows, a check pops when the save lands.

Heading with double click

activation="doubleclick" and a heading look from className: the editor takes the same 24px semibold type, so the text never moves.

Values with links

activation="button": the link stays clickable and only the pencil starts editing. renderValue draws the link.

Validation

validate keeps the editor open with the message under it and re-checks while you type.

Optimistic save, rollback

The new price shows at once with a spinner; the request fails, the old value comes back, the price shakes and the error explains why.

Inside a sentence

variant="underline" with fullWidth={false}: dashed values in running text, Enter to save, Escape to cancel.

Sizes and locales

sm, md and lg rows formatting en-US dollars, de-DE euros and ro-RO lei; the editor parses each locale's decimals.

Dependencies

motionlucide-reactui-configinputtextareanumber-inputselectbuttonspinnerkbdmotionuse-controllable-stateuse-localenumber-format

Files

  • components/ui/inline-edit.tsx

FAQ

Is the React Inline Edit component free?

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

How do I install Inline Edit?

Run npx wingo-ui@latest add inline-edit. It also adds the Wingo UI items it builds on (UIProvider, Input, Textarea, Number Input, Select 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/inline-edit.

How does Inline Edit behave on phones?

A textarea makes new lines with Enter and saves with Cmd+Enter (Ctrl+Enter on Windows); a select opens at once and saves on the pick, as a bottom sheet on phones.

See also

InputTextareaNumber InputSelectEditable GridDescription List

Used in

CSV ImportEntity HeaderAI Chat PageAutomation Builder PageCRM Pipeline Page

More React form components

Invoice line itemsRich Text EditorSchema BuilderAddress InputBooking WidgetField
All 9 React form components

Articles using this component

  • React Editable Table: Spreadsheet Grid or Inline Edit

    Component guides · Nov 5, 2026 · 9 min read

PreviousForm WizardNext Invoice line items

Your listing

Portland · posted Sep 12, 2026

Title
Price
Status
Description
All saved

Tap a value to edit it.EnterEnter saves, EscEscape cancels, a click outside saves.A tap outside saves.

buttons on · select text on · check 1200ms · limit none