Inline edit
Rename in place: the text becomes a field without moving.
pnpm dlx shadcn@latest add @uiarc/inline-editLive · keyboard ready
Activate to edit.Enter saves, Escape cancels.
Activate to edit.Enter saves, Shift+Enter adds a line break, Escape cancels.
- Titles and descriptions read far more often than edited, like a project name.
- Single fields that save on their own with optimistic updates and rollback.
- Use input or textarea in a form when several fields save together.
- Use input when the field should always look editable.
Installation
Add Inline edit with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/inline-editAdds the component and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
example.tsx
import { InlineEdit } from "@/registry/components/inline-edit/inline-edit"; export function ProjectTitle({ project }: { project: { id: string; name: string } }) { return ( <InlineEdit as="h1" label="Project name" value={project.name} validate={(next) => (next.trim() ? null : "Name can’t be empty")} onSave={(next) => renameProject(project.id, next)} /> );}InlineEdit
Click-to-edit text that becomes a field in place with the same metrics, saves optimistically, and rolls back on failure.
PropTypeDefaultDescription
valueRequiredstring–The saved value. Outside changes replace the text while not editing.onSaveRequired(next: string) => void | Promise<unknown>–Persists the value. Return a promise to show saving; reject it to roll back with a retry.labelRequiredstring–Accessible name, for example "Project name".validate(next: string) => string | null | undefined–Returns an error message to block saving.placeholderstring""Shown when the value is empty.multilinebooleanfalseWraps and grows in height; Shift+Enter adds a line break.variant"title" | "body""title"Type style: title for names and headings, body for descriptions.as"span" | "p" | "h1" | "h2" | "h3""span"Element that holds the text, so a title can stay a heading.classNamestring–Added to the root.- EnterorSpace
- On the text, starts editing.
- Enter
- Saves the draft. In multiline mode, Shift+Enter inserts a line break.
- Escape
- Cancels and rolls the text back.
- Tab
- Leaving the component saves, the way a rename does.
- The resting text is a native button labelled "<label>: <value>", so it is focusable and announced as editable.
- The field has aria-label and aria-invalid; validation and save errors render in a polite live region linked through aria-describedby.
- Save progress and results are announced through a role="status" region; save and cancel buttons are labelled.
- Text rolls between values, a check draws once a save lands, and multiline boxes follow their height; failed saves roll back with motion.
- Reduced motion crossfades text, draws the check instantly, and replaces the spinner with a static mark.
- Text wraps within the column and reserves 66px at the end for the save and cancel buttons.
- With multiline the box grows in height as the text wraps.
- Hover hints apply only on fine pointers; on touch a tap starts editing.
- Two ResizeObservers size the frame; sizes are measured in a layout effect, so no frame shows a wrong width.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use for single fields read far more often than edited, like titles and descriptions. Use a regular form with input or textarea when several fields save together.
- value is controlled by the saved data; update it after onSave resolves. Return a promise from onSave to get saving, saved, and failed states.
- Exported as both named and default.
The full library index for assistants is at /llms.txt.