ui-050

Inline Edit Field

£7 or the whole Vault, £79.20

An inline edit field: click a value to edit it in place, with optimistic saving, a pending shimmer, and rollback with Retry when a save fails.

Flip intermediate
3 more details
flip-layoutmicro-interactionform-animation

This element reads better at your own screen size than in the frame below:

Updated

About this component

An inline editor built for real, server-backed data. A compact value opens into a focused field, then collapses straight back to the new value while your async save runs. A shimmer marks the pending value, a small tick draws when it resolves, and a failed save rolls the previous value back in with an inline error and Retry. The demo shows text, email, number and URL fields, with one save that fails on its first attempt.

Inline Edit Field - UI component preview

What's included

9 items
  • Optimistic saving driven by your own async save function
  • Pending shimmer, drawn saved tick, and rollback with Retry on failure
  • Width eases between the text size and the input size
  • Native and custom validation that keeps the field open
  • Enter to save, Escape to cancel, Tab to save and move on
  • AbortSignal for superseded saves; only the latest result applies
  • InlineEdit.mount API with edit, cancel, setValue and destroy, plus events
  • Declarative data-inline-edit markup mounts itself
  • Reduced-motion support and light and dark themes

Perfect for

4 use cases
  • Editable account and profile details saved straight to an API
  • Record names and contact fields in dashboards and admin tools
  • Settings that should feel instant but must survive a failed request
  • Project metadata that stays readable between edits

How it works

1 section

Select a value to edit it in place. Saving validates the input, shows the new value immediately and calls your async save with an AbortSignal. A resolved save draws a tick; a rejected save rolls the previous value back in and offers Retry. Events report each save, success and failure.

Plugins Flip
Difficulty Intermediate
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this UI element's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Paid UI element

Purchase to unlock the code.

Buying Inline Edit Field opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£7 Standard license, unlimited projects

Browse UI elements

Or the whole library: the Vault, £79.20, one payment.

Documentation

What's Included

  • index.html: four fields (text, email, number, URL) wired to simulated network saves. The website save fails on its first attempt so the rollback and Retry are visible.
  • assets/style.css: component styles and the two theme token sets.
  • assets/script.js: readable source exposing window.InlineEdit.
  • assets/script.min.js: minified production build.
  • Width that eases between the text size and the input size, with neighbouring fields sliding.
  • A pending shimmer across the new value, a saved tick that draws and fades, and a rollback that rolls the previous value back in with a short shake and an inline error line with Retry.
  • Native and custom validation that keeps the field open with a message.
  • Independent instances, AbortSignal support for superseded saves, and a destroy() that reverts everything.

Quick Start

Load the stylesheet in your head:

Code snippet omitted: it ships with the download.

Copy one .inline-edit[data-inline-edit] block from index.html. Give each instance unique IDs, update the matching ARIA references, and set the input's name, type and initial value alongside the displayed text.

Add the dependencies and component script before </body>:

Code snippet omitted: it ships with the download.

Every [data-inline-edit] element mounts itself on DOMContentLoaded. Attach your save function through the API:

Code snippet omitted: it ships with the download.

Calling mount() on an element that has already mounted returns the same instance with the new options merged in, so it does not matter whether your code runs before or after the automatic mount. Use data-inline-edit="manual" to skip the automatic mount entirely.

Keyboard & Accessibility

Key Action
Enter or Space on the value button Opens the editor and selects its value
Enter in the input Validates and saves
Tab in the input Validates, saves, and moves focus to the next focusable element after the field
Escape inside the form Discards the edit and restores the displayed value
Enter in a textarea Inserts a new line; Ctrl+Enter or Cmd+Enter saves
Tab to Retry, then Enter Re-runs a failed save

Saving with Enter or the Save button, and cancelling, return focus to the value button. Clicking outside leaves the draft open. The field root carries aria-busy="true" while a save is pending. Native labels and buttons provide names; aria-invalid and a role="alert" message explain invalid values. A role="status" region announces "Saving.", "Saved.", "Edit cancelled." and the failure message with "The previous value was restored." Disabled value buttons cannot open. Under reduced motion every state still happens, instantly, and the pending shimmer becomes a static dim.

How It Works

  1. Selecting the value hides the button and shows the form; Flip eases the surface from its text width to the full input width.
  2. On save the value is validated: native constraints first (required, type, min, max, maxlength), then your validate function. An error keeps the field open.
  3. A valid, changed value is shown immediately (optimistic) and the field collapses back. data-state="pending" runs a masked shimmer across the new text while your save promise runs.
  4. Resolve: the tick draws with a stroke offset tween and fades after a moment. If save resolves to a string, that string becomes the value (useful when the server normalises it).
  5. Reject: the new value rolls out, the previous value rolls back in, the button shakes briefly, data-state="error" tints the border, and the error line shows the rejection's message with Retry. Retry rolls the attempted value back in and runs save again. Opening the editor from the error state pre-fills the failed value so it can be corrected.
  6. Saving again while a request is pending aborts the earlier request's signal; only the latest save's outcome is applied.

Without a save option the field saves locally and confirms immediately.

API

Code snippet omitted: it ships with the download.

Option Type Description
save (value, { previous, signal, name }) => Promise Persist the value. Reject (throw) to roll back; the error's message is shown. Resolve to a string to replace the value
validate (value, { previous, name, input }) => string Return an error message to keep the field open, or an empty string to accept
emptyLabel string Text shown for an empty value. Defaults to data-empty-label, then "Add a value"
Method or property Description
field.edit() Open the editor
field.cancel() Close the editor and discard the draft
field.setValue(value) Set the confirmed value without saving; aborts a pending save and clears an error
field.destroy() Abort any request, remove listeners and injected nodes, and revert inline styles
field.value The last confirmed value
field.state "idle", "editing", "pending" or "error"
InlineEdit.mountAll(scope?) Mount every [data-inline-edit] in scope (the automatic mount calls this)
InlineEdit.get(element) The instance for an element, or null
InlineEdit.destroyAll() Destroy every instance

Events

All events bubble from the field root.

Event detail
inlineedit:edit { name, value }
inlineedit:save { name, value, previous }, fired as the optimistic save starts
inlineedit:saved { name, value, previous, result }
inlineedit:error { name, value, previous, error }
inline-edit:save { name, value, previousValue }, kept from the previous version and now fired after a save resolves

Upgrading from the previous version

  • The markup hooks are unchanged. [data-edit-failure] (with [data-edit-failure-text] and [data-edit-retry]) is new and is created automatically when missing. For the drawn tick, give the saved icon an inline <path pathLength="1"> as in index.html; a <use> icon still fades in.
  • window.inlineEditContext is removed. Call InlineEdit.destroyAll() (or destroy() per instance) on teardown.
  • inline-edit:save still fires, but only once the save has succeeded.
  • The resting surface now sizes to its text (width: fit-content) instead of a fixed 320px.

Using It With Your Own Design

The demo toolbar, the simulated request() saves in index.html and the .stage / .stage__inner layout are demo furniture. Keep the tokens and .inline-edit rules.

The optional .inline-edit-group[data-inline-edit-group] lays fields out in a responsive two-column grid, and neighbouring fields slide as one opens or closes. Each field owns its own form, so use a section or div as the group, not another form. .inline-edit--wide spans both columns.

Customisation

  • Set native input required, type, min, max, maxlength and name attributes. Required, email, URL, number and range errors have specific messages; other constraints use the browser's message. A textarea with data-edit-input supports multiline values.
  • Add disabled to the value button to prevent editing.
  • .inline-edit__surface min-width sets the narrowest resting width.
  • The shimmer is the inline-edit-shimmer keyframes and mask gradient; the failure tint is .inline-edit[data-state="error"] .inline-edit__surface.
  • Durations live in morph(), roll(), showTick() and shake() in script.js.

Themes

Ships in dark (the demo default) and light. Put the attribute on the page body or an individual component:

Code snippet omitted: it ships with the download.

The demo supports ?variant=light and ?variant=dark. Component JS never reads the theme name.

Re-value --ground, --ground-2, --raised, --hover, --ink, --ink-2, --ink-3, --line, --line-strong, --accent, --accent-ink, --error, --shadow, --font, --radius, --radius-sm and --radius-xs in either theme block for your product. Keep text contrast at least 4.5:1.

Requirements

  • GSAP 3.12+ and Flip.
  • Modern browser; no build step or framework required.
  • JavaScript for editing; initial values remain readable without it.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.