More Forms Snippets
Inline Edit Field — Click-to-Edit UI Snippet
Inline Edit Field · Forms · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Inline Edit Field — Click-to-Edit Text, Textarea & Date with Save/Cancel

An inline edit field (also called click-to-edit or in-place editing) is one of the most-searched SaaS UI patterns because it lets users edit a value without navigating away to a separate form page — the text itself becomes the field when clicked. The editable table applies the same pattern to every cell. This snippet provides a complete, production-quality implementation with three field types (plain text, textarea, and date), save and cancel actions, Escape-key cancellation, a "Saved" toast notification, and original-value restoration on cancel.
The view/edit toggle model
Each field has two states rendered in the same container: a "view" span (the read-only text with a pencil icon) and an "edit" section (the input with action buttons). startEdit() hides the view and shows the edit, moving focus into the input and selecting text for a text field. save() and cancel() both restore the view, with save() writing the new value and cancel() restoring the original from the originals cache.
Saving the original for cancel
Before switching to edit mode, startEdit() caches the input's current value in an originals map keyed by field id. cancel() reads this cache to restore the exact pre-edit value, so users can explore or partially edit a field and get back exactly what was there before, even across multiple fields open and cancelled in sequence.
Display formatters
The date field stores an ISO date (YYYY-MM-DD) in the input because that is what a date input requires, but the view should show a human-readable string ("December 31, 2025"). A formatters map holds per-field display functions: save() runs the formatter (if any) on the input value before writing it to the view text. This pattern cleanly separates stored/input format from display format for any field type.
Global Escape handler
A document-level keydown listener cancels any open edit when Escape is pressed, walking all fields and calling cancel() on whichever is in edit mode. This is an expected browser convention for dismissing an in-progress edit and is easy to miss in custom implementations.
Saved toast
A fixed-position toast slides up and fades in on every successful save, then auto-dismisses after two seconds. It uses CSS transition on opacity and translateY for the animation, driven purely by adding and removing a class — no animation library needed.
Accessibility and validation
Empty values are rejected in save() — the input is focused back and the save is aborted. The pencil icon and hover-highlight communicate editability. In a production system you would also add aria-label attributes and manage focus return to the view element after saving.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You do not need to work out the original-value snapshot logic entirely on your own. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the originals cache in startEdit lets cancel restore the exact pre-edit value even across repeated edit-and-cancel cycles, or why the formatters map is checked only inside save rather than also being applied when the field first renders. The same assistant can help you optimize it — ask whether the document-level Escape listener that walks every .field on every keypress could instead track just the currently open field to avoid unnecessary DOM queries. It is just as useful for extending the pattern: ask it to add a select-type field for choosing from a fixed list, inline validation that blocks save on an empty or malformed value before the toast fires, or optimistic UI that rolls a field back automatically if a real API call fails after save. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a click-to-edit inline field group in plain HTML, CSS, and JavaScript with no framework and no libraries.
Requirements:
- Each field renders two states inside the same container: a view state (the current text plus a pencil icon, clickable) and an edit state (an input or textarea plus Save and Cancel buttons), with only one visible at a time via inline display toggling.
- Support at least three field types sharing the same toggle logic: a plain text input, a multi-line textarea, and a native date input.
- Before switching a field into edit mode, cache the input's current value in a lookup object keyed by the field's id, so Cancel can restore that exact snapshot regardless of how many times the field has previously been edited and saved.
- Support a per-field formatter function (keyed the same way as the value cache) that transforms the raw input value into a human-readable display string only at save time — for example converting an ISO date like 2025-12-31 into "December 31, 2025" for the view text, while the underlying input keeps the ISO value.
- Reject saving an empty or whitespace-only value: refocus the input and do not switch back to the view state.
- Add a single global keydown listener on the document that, when Escape is pressed, finds whichever field currently has its edit state visible and cancels it, restoring the original value.
- On every successful save, show a small fixed-position toast that reads "Saved", sliding up and fading in via a CSS transition driven by adding and removing one class, then auto-dismissing after about two seconds.Step by step
How to Use
- 1Click any field to edit itClick the text or the pencil icon to switch to edit mode. The field highlights on hover to communicate that it is editable.
- 2Edit and saveType in the input or textarea, then click Save. The view updates with the new value and a "Saved" toast confirms the action.
- 3Cancel or press EscapeClick Cancel or press Escape to discard changes. The field restores its exact original value.
- 4Add your own fieldDuplicate a .section block. Add a formatter function to the formatters map if the stored value needs a different display format (like the ISO date).
- 5Wire to an APIIn save(), after updating the view, call your API with the new value. On error, call cancel() to roll back and show an error message.
- 6Export for your frameworkClick "React" for a component using useState for each field's edit mode and value. Click "Vue" for a Vue 3 SFC with reactive state.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Before switching to edit mode, startEdit() stores the input's current value in an originals object keyed by field id. cancel() reads originals[id] and writes it back to the input, then re-renders the view text. This snapshot is taken at the moment editing begins, so it captures the last saved value regardless of how many times the user has previously edited and saved the field.
A native date input stores and emits values in ISO 8601 format (YYYY-MM-DD) because that is what the HTML spec requires. But the view should show "December 31, 2025" rather than "2025-12-31". The formatters map lets each field define a display function that transforms the raw input value for the view. save() checks for a formatter and applies it before writing to the view text element.
Add a check in startEdit() that calls cancel() on any currently open field before opening the new one. Walk the fields, find one whose edit section is visible, and cancel it first. This ensures only one field is editable at a time, which is the expected SaaS convention and avoids users accidentally abandoning edits.
For each field, keep editMode (boolean) and draftValue (string) in useState. On click, set editMode to true and draftValue to the current value. The component renders either the view span or the input based on editMode. save() validates, calls your API with draftValue, updates the persisted value in state, and sets editMode to false. cancel() just sets editMode to false — the draftValue is discarded and the persisted value is displayed again. For the Tailwind version, click "Tailwind" to get the same markup with utility classes instead of a scoped stylesheet.