HyInlineEdit
Usage examples on this page are written for Lit / plain HTML (
<hy-inline-edit>). The same component ships asHyInlineEditin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
Click-to-edit text: a display ⇄ edit swap with an async commit lifecycle.
The value of this component is encapsulating the error-prone commit logic — Enter / Escape / blur-commit with a relatedTarget guard, optional confirm/cancel buttons, async validation that keeps the edit view open, and an optimistic-commit-then-rollback contract via a cancelable change event. It is editor-agnostic: the default editor is hy-text-input, but any hy-* form control can be slotted into editor.
Key Features:
trigger:click/dblclick/manual(pencil button or programmatic)affordance:hover/always/none—nonedrops the pencil and makes the text itself the control (Tab, Enter / Space, a hover outline the focus ring recolours)hint: a tooltip over the display view ("Select to rename") — the spoken cue where no glyph says the text is editableemphasis:outlined(thehy-text-inputcontrol) /plain(seamless: a bare input in the text's own box — same font, same line, width = its text — so the display ⇄ edit swap moves nothing; the Google Docs title)submit-on:blur/enter/confirm(Escape always reverts)- Editor-agnostic
editorslot (defaults tohy-text-input) validatekeeps the edit view open with an inline errorloading+ cancelablechange→ optimistic update with rollback- Display ≠ edit: the default slot renders formatted display; the editor edits the raw value
Examples
Basic rename
<hy-inline-edit value="Untitled"></hy-inline-edit>Text-only title (no pencil) — a document name in a header
<hy-inline-edit
value="Untitled"
affordance="none"
hint="Select to rename"
label="Document name"
submit-on="confirm"
>
<span class="hy-typography" data-hy-style="title-xs">Untitled</span>
</hy-inline-edit>Seamless title — no pencil, no buttons, no layout shift (Google Docs)
<!-- The typography sits on the HOST so the plain editor inherits it too. -->
<hy-inline-edit
class="hy-typography"
data-hy-style="title-xs"
value="Untitled"
affordance="none"
emphasis="plain"
submit-on="blur"
hint="Select to rename"
label="Document name"
></hy-inline-edit>Async commit with rollback
<hy-inline-edit value="Untitled" submit-on="blur"></hy-inline-edit>
<script>
el.addEventListener('change', (e) => {
e.preventDefault(); // keep the edit view open
el.loading = true;
save(e.detail.value).then(
() => { el.value = e.detail.value; el.editing = false; el.loading = false; },
() => { el.loading = false; } // stay open on failure
);
});
</script>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
value | value | string | undefined | — | The committed value. |
editing | editing | boolean | false | Controlled open state of the edit view. |
trigger | trigger | 'click' | 'dblclick' | 'manual' | 'click' | How edit mode opens. |
submitOn | submit-on | 'blur' | 'enter' | 'confirm' | 'blur' | When a draft is committed. |
affordance | affordance | 'hover' | 'always' | 'none' | 'hover' | When the edit affordance (pencil) is visible. hover reveals it on hover / focus (ideal for dense lists where a persistent pencil per row is clutter); always keeps it visible at rest (prominent single fields where editability must be discoverable); none renders no pencil at all and makes the text itself the control — a button reachable by Tab, opened by click / Enter / Space, an outline on hover, recoloured by focus, standing in for the glyph, focus handed back to it after Enter / Escape / the confirm buttons. The shape of a document or page title in a header (Google Docs, Notion, Atlassian's inline edit); it needs a trigger other than manual to be reachable. |
loading | loading | boolean | false | Async pending — shows a spinner, disables the editor. |
placeholder | placeholder | string | undefined | — | Empty-value placeholder shown in the display view. |
disabled | disabled | boolean | false | Whether the control is disabled. |
label | label | string | undefined | — | Accessible label forwarded to the default editor. |
emphasis | emphasis | 'outlined' | 'plain' | 'outlined' | The default editor's chrome. outlined (default) is the hy-text-input control — a full form control with its own height and padding. plain is the seamless editor: a bare input inheriting the host's font, size, weight, letter-spacing and line-height, no padding / border / background, width sized to its text (a hidden mirror span), the focus ring its only chrome — so the display ⇄ edit swap changes nothing around it. A click opens it with the caret where the click landed; with affordance="none" keyboard focus opens it directly with everything selected (Tab onto the name and type to replace — no Enter first). Put the typography on the host (not on a slotted span) so both views read it. A slotted editor wins over either. |
hint | hint | string | undefined | — | Tooltip shown over the display view on hover / focus (e.g. "Select to rename"). The display view is wrapped in an hy-tooltip only while the hint is set, and never while editing. Pairs with affordance="none", where no glyph says the text is editable; the tooltip also becomes the trigger's accessible description. |
validate | — | InlineEditValidator | undefined | — | Validator run before commit; a returned message keeps the edit view open. Property-only. |
Events
| Event | Detail | Description |
|---|---|---|
edit-start | — | Fired when the edit view opens. Detail: {} |
change | — | Commit. Cancelable — preventDefault() keeps the edit view open. Detail: { value: string } |
cancel | — | Fired when editing is canceled (Escape / cancel button). Detail: {} |
Slots
| Slot | Description |
|---|---|
default | The display rendering of the rest state (formatted). Falls back to value / placeholder. |
editor | The edit control (defaults to hy-text-input, or a bare input under emphasis="plain"); a slotted editor wins over both. |
edit-trigger | The pencil / edit affordance (defaults to a hy-icon-button). Not rendered under affordance="none". |
CSS Parts
| Part | Description |
|---|---|
display | The display-view wrapper. |
edit | The edit-view wrapper. |
error | The inline validation error. |