Skip to content
hightouchUI

Design system

v42.1.0

Editable text

Editable text allows user to edit text in the same place as it's displayed — for changing a label or name in place without a form field.

Example

Usage

Truncated text

The preview value and input should automatically fit the parent container's space. If a specific width is wanted, use the width prop.

Left action

Use leftAction to show a custom button before the confirm and cancel buttons when editing. An icon indicator appears inline when usedActionTooltip is provided, and the button can be shown on empty values via showOnEmpty.

Minimal

Set variant="minimal" to show the text on its own: no edit icon, no confirm and cancel buttons, and no underline while editing. Use it in a list, where those controls on every row would compete with the rows themselves. Enter saves and Escape cancels.

A single click opens editing with the cursor at the end of the value. A double-click selects the whole value, so typing replaces it.

The text opens its own editor by default. Add isEditing when the row around it should decide when editing starts — see Controlled below.

Controlled

Use isEditing and onEditingChange when something other than the text starts the edit. A sidebar of past agent chats is the case this exists for: the row is a link that opens the chat, and a double-click renames it in place. Without this, every click on the title would open an edit instead of the chat.

The text stops responding to clicks and leaves the tab order, so give people another way in. Below, a rename button carries the keyboard path that the double-click cannot. Because the row opens the editor rather than the text itself, the name arrives selected and ready to replace.

If the row navigates, put the link beside the text rather than around it. An input inside an <a> or <button> is invalid, and every click meant for the field activates the row instead. Give the row position: relative and let the link cover it with an absolutely positioned ::before, so the field stays clickable above it.

Disabled

Skeleton

The editable text has a skeleton state.

Size

The size prop sets the text size. The default is md.

Font weight

Use fontWeight to match the text around it, such as a name that reads as a title in its row.

Combinations

One chat name throughout, in the four states a sidebar row can be in. Hover any cell in the default row to reveal its edit icon — the minimal row has none, which is the difference between them.

Initial
Empty
Long value
Disabled
Default
Retention model
Set a name
Quarterly retention analysis for returning customers
Retention model
Minimal
Retention model
Set a name
Quarterly retention analysis for returning customers
Retention model
Left action
Retention model
Set a name
Quarterly retention analysis for returning customers
Retention model

Guidelines

When to use

  • For text that is read far more often than it is edited, such as a resource name in a list or a detail header.
  • When the value belongs where it is shown, and a separate form would take people away from it.
  • With isEditing when something outside the text starts the edit — a sidebar row that is also a link, or a rename item in a menu.
  • With variant="minimal" in a list, where buttons repeated on every row would crowd the page.

When not to use

  • If the text is edited frequently or as part of a form, use a text input in a form field instead.
  • If editing a resource name displayed as a heading, use an editable heading instead.
  • If editing a multi-line description of a resource, use an editable description instead.
  • If nothing outside needs to start or stop the edit, leave isEditing off and let a click on the text start it. isEditing means you must add your own keyboard trigger.

Accessibility

By default the text is a tab stop, and Enter or Space starts an edit. isEditing removes that tab stop, so you must add a keyboard trigger yourself: a double-click, or a button that only shows on hover, leaves keyboard users with no way to start an edit, and automated accessibility checks do not catch this.

When the edit ends, focus returns to whatever started it, unless it has already moved elsewhere.

Props

Inherits Text props.

NameDefaultDescription
fontWeight"normal""normal" | "medium" | "semibold"Font weight
isEditing

booleanWhether the field is being edited. Omit it and the component owns edit state, opening when the label is clicked. Pass it together with onEditingChange — the pairing is enforced by the type — to own that state from outside: the component then never opens itself — nothing about the label is clickable or focusable, and the hover edit icon is hidden — so it can sit inside a row that has its own click behavior. isDisabled wins over it, and a disabled field never opens an editor.
onEditingChange

(isEditing: boolean) => voidCalled with false when the user finishes editing — Enter, Escape, blur, or a click outside. Required alongside isEditing, and it has to be honored, or the field can never close.
variant"default""default" | "minimal"Visual mode. minimal shows just the text: no hover edit icon, no confirm and cancel buttons, and no underline while editing. Use it in a list, where controls on every row would crowd the page — people press Enter to save and Escape to cancel instead. leftAction is ignored in this mode.
leftAction

EditableLeftActionPropsShow a custom button before the confirm and cancel buttons. The object passed to this prop should be stable between renders to avoid unnecessary re-renders. Ignored when variant is minimal.
inputProps

Pick<InputProps, "maxLength" | "minLength">The input props
isDisabledfalsebooleanDetermines if text is editable.
placeholder

stringThe placeholder text to use when the field is empty.
size"md""sm" | "md" | "lg"Size of the text.
value

stringText value.
widthundefined"100%" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl"Width of the component.
onChange

(value: string) => voidCallback invoked when user confirms the new value.
onCancel

(value: string) => voidCallback invoked when user cancels input with the Esc key. It provides the last confirmed value as argument.