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.
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
isEditingwhen 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
isEditingoff and let a click on the text start it.isEditingmeans 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.
| Name | Default | Description |
|---|---|---|
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 |
isDisabled | false | booleanDetermines 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. |
width | undefined | "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. |