Skip to content

Fields

Editable

Plain text that turns into an input when clicked.

When to use it: Use it to rename something in place, such as a document title or a list name.

On this page

Example

Loading example…
Editable.tsxtsx
import { Editable, EditableInput, EditableView } from "@comp0/react";
import { PencilIcon } from "@heroicons/react/16/solid";

export function Example() {
  return (
    <Editable as="div" className="w-full max-w-xs" defaultValue="Untitled document">
      <EditableView className="group flex w-full items-center gap-2 rounded px-3 py-2.5 text-left text-base font-medium text-zinc-900 outline-teal-600 hover:bg-zinc-100 focus-visible:outline-2 data-empty:font-normal data-empty:text-zinc-400 sm:py-2 sm:text-sm dark:text-zinc-100 dark:outline-teal-400 dark:hover:bg-zinc-800">
        {({ value }) => (
          <>
            <span className="truncate">{value || "Untitled document"}</span>
            <PencilIcon
              className="size-4 shrink-0 text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-300"
              aria-hidden="true"
            />
          </>
        )}
      </EditableView>
      <EditableInput
        aria-label="Document title"
        name="title"
        className="w-full rounded border border-zinc-950/10 bg-white px-3 py-2.5 text-base font-medium text-zinc-950 outline-teal-600 focus-visible:outline-2 sm:py-2 sm:text-sm dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-50 dark:outline-teal-400"
      />
    </Editable>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. Editable

    Context provider with no DOM by default. Does not add a DOM element.

  2. EditableView

    Native button showing the committed value; click enters edit mode. Owns a DOM element.

  3. EditableInput

    Native input that stays in the DOM so its name always submits. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Wrap Editable around EditableView and EditableInput.

  2. 2

    Add the supporting parts

    EditableView shows the committed value and enters edit mode on click; EditableInput takes over while editing.

  3. 3

    Make the behavior clear

    Enter commits, Escape cancels, and clicking away commits; give the input a name so a form submits the committed value.

    Exampletsx
    <Editable defaultValue="Untitled document">
      <EditableView />
      <EditableInput name="title" aria-label="Document title" />
    </Editable>;

Keyboard

Enters edit mode. · view
Commits the draft and returns to the view. · input
Esc
Cancels editing and restores the committed value. · input
Moves focus away; leaving the input commits the draft.

Forms and accessibility

The always-present EditableInput submits its native name with the committed value.

Accessibility checklist

  • EditableView is a real button, so keyboard users reach it with Tab and press Enter to start editing.
  • Give EditableInput an aria-label that names the value, such as Document title; the view's text is hidden while editing.
  • Show the edit affordance, such as a pencil icon, without relying on hover alone.
  • Style [data-empty] on EditableView so an empty value still leaves something visible to click.

API reference

Importtsx
import { Editable, EditableInput, EditableView } from "@comp0/react";

Editable

Context only

Context provider with no DOM by default.

PropTypeDescription
valuestringControlled committed value.
defaultValuestringInitial committed value.
onChange(value: string) => voidReceives the committed value when an edit commits, not per keystroke.
editingbooleanControlled edit mode.
defaultEditingbooleanInitial edit mode.
onEditingChange(editing: boolean) => voidReceives the next edit mode.
disabledbooleanBlocks entering edit mode and disables both parts.

EditableView

DOM element

Native button showing the committed value; click enters edit mode.

PropTypeDescription
childrenReactNode | (state) => ReactNodeCustom display; a function receives { value, editing }.

EditableInput

DOM element

Native input that stays in the DOM so its name always submits.

PropTypeDescription
namestringSubmission name for the committed value.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

Editable

Style hookMeaning
[data-editing]An edit is in progress.
[data-disabled]Editing cannot start.

EditableView

Style hookMeaning
[data-editing]An edit is in progress.
[data-empty]The committed value is empty, so a placeholder can be styled.
[data-disabled]Editing cannot start.

EditableInput

Style hookMeaning
[data-editing]An edit is in progress.
[data-disabled]Editing cannot start.

Keep exploring