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.
Example
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.
Editable
Context provider with no DOM by default. Does not add a DOM element.
EditableView
Native button showing the committed value; click enters edit mode. Owns a DOM element.
EditableInput
Native input that stays in the DOM so its name always submits. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap Editable around EditableView and EditableInput.
- 2
Add the supporting parts
EditableView shows the committed value and enters edit mode on click; EditableInput takes over while editing.
- 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
import { Editable, EditableInput, EditableView } from "@comp0/react";Editable
Context onlyContext provider with no DOM by default.
| Prop | Type | Description |
|---|---|---|
value | string | Controlled committed value. |
defaultValue | string | Initial committed value. |
onChange | (value: string) => void | Receives the committed value when an edit commits, not per keystroke. |
editing | boolean | Controlled edit mode. |
defaultEditing | boolean | Initial edit mode. |
onEditingChange | (editing: boolean) => void | Receives the next edit mode. |
disabled | boolean | Blocks entering edit mode and disables both parts. |
EditableView
DOM elementNative button showing the committed value; click enters edit mode.
| Prop | Type | Description |
|---|---|---|
children | ReactNode | (state) => ReactNode | Custom display; a function receives { value, editing }. |
EditableInput
DOM elementNative input that stays in the DOM so its name always submits.
| Prop | Type | Description |
|---|---|---|
name | string | Submission 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 hook | Meaning |
|---|---|
[data-editing] | An edit is in progress. |
[data-disabled] | Editing cannot start. |
EditableView
| Style hook | Meaning |
|---|---|
[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 hook | Meaning |
|---|---|
[data-editing] | An edit is in progress. |
[data-disabled] | Editing cannot start. |