Editable
Inline text editing: click the preview to edit, Enter/blur to commit, Escape to discard. Pair with Editable.Area for multiline. Controlled and uncontrolled.
Features
Example
Anatomy
import { Editable } from '@wire-ui/react'
<Editable.Root defaultValue="Click to edit me" placeholder="Enter some text…">
<Editable.Preview />
<Editable.Input />
</Editable.Root>Multiline
Swap Editable.Input for Editable.Area to edit longer text in a <textarea>. In multiline mode a plain Enter inserts a newline — commit with Cmd/Ctrl+Enter (or blur, unless submitOnBlur is false).
<Editable.Root defaultValue="A longer description…">
<Editable.Preview />
<Editable.Area rows={3} />
</Editable.Root>Styling
Editable ships no styles. Editable.Root sets data-editing while editing and data-disabled when disabled. Editable.Preview sets data-empty when the committed value is empty — use it to style placeholder text.
<Editable.Preview className="
text-black
data-[empty]:text-[#9ca3af]
" />
<Editable.Root className="
data-[disabled]:opacity-50 data-[disabled]:cursor-not-allowed
" />Note that Editable.Preview is only rendered while not editing, and Editable.Input / Editable.Area / Editable.SubmitTrigger / Editable.CancelTrigger only while editing, so they never overlap.
Using data attributes
/* Placeholder styling on an empty preview */
[data-empty] { color: #9ca3af; }
/* Disabled root */
[data-disabled] { opacity: 0.5; cursor: not-allowed; }Root props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled committed value |
defaultValue | string | '' | Initial committed value (uncontrolled) |
onChange | (value: string) => void | — | Called when the committed value changes |
editing | boolean | — | Controlled editing state |
defaultEditing | boolean | false | Initial editing state (uncontrolled) |
onEditingChange | (editing: boolean) => void | — | Called when editing starts or stops |
onSubmit | (value: string) => void | — | Called with the new value when an edit is committed |
onCancel | () => void | — | Called when an edit is discarded |
onEdit | () => void | — | Called when editing begins |
submitOnBlur | boolean | true | Commit when the field loses focus |
disabled | boolean | false | Prevent editing |
placeholder | string | — | Shown by Preview when the value is empty |
Sub-component props
| Component | Element | Notes |
|---|---|---|
Editable.Preview | <span role="button"> | Click or Enter/Space to start editing; only shown when not editing |
Editable.Input | <input type="text"> | Single-line field; only shown while editing |
Editable.Area | <textarea> | Multiline field; only shown while editing |
Editable.EditTrigger | <button type="button"> | Starts editing; only shown when not editing |
Editable.SubmitTrigger | <button type="button"> | Commits the edit; only shown while editing |
Editable.CancelTrigger | <button type="button"> | Discards the edit; only shown while editing |
Input and Area manage their own value/onChange/defaultValue from context, so those props are omitted from their types; all other native attributes pass through.
Data attributes
| Attribute | Element | Values |
|---|---|---|
data-editing | Root | Present while editing |
data-disabled | Root | Present when disabled |
data-empty | Preview | Present when the committed value is empty |
Preview also sets role="button", tabindex (0, or -1 when disabled), and aria-disabled when disabled.
Accessibility
Editable.Previewis exposed as a button (role="button", focusable) and starts editing on Enter or SpaceInputandAreareceive focus and select their contents automatically when editing begins- When disabled, the preview is removed from the tab order (
tabindex="-1") and markedaria-disabled
Keyboard Interactions
| Key | Description |
|---|---|
| Enter / Space | On a focused preview, starts editing. |
| Enter | In single-line Input, commits the edit. |
| Cmd/Ctrl+Enter | In multiline Area, commits the edit. |
| Escape | Discards the edit and restores the previous value. |
| Tab | Moves focus to the field or the next trigger. |