{"slug":"editable","title":"Editable","description":"Using the editable machine in your project.","contentType":"component","framework":"react","content":"Editable is an input field used for editing a single line of text. It renders as\nstatic text and transforms into a text input field when the edit interaction is\ntriggered (click, focus, or double-click).\n\n## Resources\n\n\n[Latest version: v1.35.3](https://www.npmjs.com/package/@zag-js/editable)\n[Logic Visualizer](https://zag-visualizer.vercel.app/editable)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/editable)\n\n\n\n**Features**\n\n- Use custom controls for the editable\n- Pressing `Enter` commits the input value\n- Pressing `Esc` reverts the value\n- Activate edit mode by double-clicking or focusing on the preview text\n- Auto-resize input to fit content\n\n## Installation\n\nInstall the editable package:\n\n```bash\nnpm install @zag-js/editable @zag-js/react\n# or\nyarn add @zag-js/editable @zag-js/react\n```\n\n## Anatomy\n\nCheck the editable anatomy and part names.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the editable package:\n\n```jsx\nimport * as editable from \"@zag-js/editable\"\n```\n\nThe editable package exports two key functions:\n\n- `machine` - Behavior logic for the editable.\n- `connect` - Maps behavior to JSX props and event handlers.\n\n> Pass a unique `id` to `useMachine` so generated element ids stay predictable.\n\nThen use the framework integration helpers:\n\n```jsx\nimport * as editable from \"@zag-js/editable\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\n\nexport default function Editable() {\n  const service = useMachine(editable.machine, { id: \"1\" })\n\n  const api = editable.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <div {...api.getAreaProps()}>\n        <input {...api.getInputProps()} />\n        <span {...api.getPreviewProps()} />\n      </div>\n    </div>\n  )\n}\n```\n\n### Setting the initial value\n\nSet `defaultValue` to define the initial value.\n\n```jsx {2}\nconst service = useMachine(editable.machine, {\n  defaultValue: \"Hello World\",\n})\n```\n\n### Controlled value\n\nUse `value` and `onValueChange` to control the value externally.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  value,\n  onValueChange(details) {\n    setValue(details.value)\n  },\n})\n```\n\n### Controlled edit state\n\nUse `edit` and `onEditChange` to control whether the field is in edit mode.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  edit,\n  onEditChange(details) {\n    setEdit(details.edit)\n  },\n})\n```\n\n### Listening for value changes\n\nEditable supports two ways of listening for value changes:\n\n- `onValueChange`: called when value changes.\n- `onValueCommit`: called when the value is committed.\n\n```jsx {2-4}\nconst service = useMachine(editable.machine, {\n  onValueChange(details) {\n    console.log(\"Value changed\", details.value)\n  },\n  onValueCommit(details) {\n    console.log(\"Value submitted\", details.value)\n  },\n})\n```\n\n### Listening for revert events\n\nUse `onValueRevert` to react when a user cancels editing.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  onValueRevert(details) {\n    console.log(\"Value reverted\", details.value)\n  },\n})\n```\n\n### Using custom controls\n\nIn some cases, you might need to use custom controls to toggle the edit and read\nmode. We use the render prop pattern to provide access to the internal state of\nthe component.\n\n```jsx\nimport * as editable from \"@zag-js/editable\"\nimport { useMachine } from \"@zag-js/react\"\n\nexport default function Editable() {\n  const service = useMachine(editable.machine, { id: \"1\" })\n\n  const api = editable.connect(service)\n\n  return (\n    <div {...api.getRootProps()}>\n      <div {...api.getAreaProps()}>\n        <input {...api.getInputProps()} />\n        <span {...api.getPreviewProps()} />\n      </div>\n      <div>\n        {!api.editing && <button {...api.getEditTriggerProps()}>Edit</button>}\n        {api.editing && (\n          <div>\n            <button {...api.getSubmitTriggerProps()}>Save</button>\n            <button {...api.getCancelTriggerProps()}>Cancel</button>\n          </div>\n        )}\n      </div>\n    </div>\n  )\n}\n```\n\n### Auto-resizing\n\nSet `autoResize` to `true` to auto-grow the editable as content changes.\n\n```jsx {2}\nconst service = useMachine(editable.machine, {\n  autoResize: true,\n})\n```\n\nWhen using autoresize, the input and preview elements should not have any\nstyles. Use `all: unset` if needed and pass any styles to the \"area\" element\nsince it's shared by the input and preview elements.\n\n### Setting a maxWidth\n\nIt is common to set a max width when auto-resizing.\n\n```jsx {2-3}\nconst service = useMachine(editable.machine, {\n  autoResize: true,\n  maxWidth: \"320px\",\n})\n```\n\nWhen the editable reaches the specified max-width, it'll clip the preview text\nwith an ellipsis.\n\n### Editing with double click\n\nThe editable supports two modes of activating the \"edit\" state:\n\n- when the preview part is focused (with pointer or keyboard).\n- when the preview part is double-clicked.\n\nSet `activationMode` to `\"dblclick\"` to only enter edit mode on double click.\n\n```jsx {2}\nconst service = useMachine(editable.machine, {\n  activationMode: \"dblclick\",\n})\n```\n\n### Customizing submit behavior\n\nUse `submitMode` to control when edits are committed.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  submitMode: \"enter\", // \"enter\" | \"blur\" | \"both\" | \"none\"\n})\n```\n\n### Usage with Textarea\n\nEditable supports using a `textarea` instead of an `input` field. When a\ntextarea is used, the editable will commit the value on `Cmd + Enter` or\n`Ctrl + Enter`.\n\n> Use `api.getInputProps()` to spread input props to the textarea. You might\n> need to cast the input props to the correct type.\n\n```tsx {2}\n<textarea\n  {...(api.getInputProps() as HTMLTextareaProps<HTMLTextareaElement>)}\n/>\n```\n\n### Customizing placeholder text\n\nSet `placeholder` as a string or per-mode object.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  placeholder: {\n    preview: \"Click to edit\",\n    edit: \"Type a value\",\n  },\n})\n```\n\n### Customizing accessibility labels\n\nUse `translations` to customize control labels.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  translations: {\n    edit: \"Edit value\",\n    submit: \"Save\",\n    cancel: \"Cancel\",\n    input: \"Editable input\",\n  },\n})\n```\n\n### Submitting with an external form\n\nSet `form` if the hidden input should submit with a form outside the current DOM\nsubtree.\n\n```jsx\nconst service = useMachine(editable.machine, {\n  name: \"value\",\n  form: \"checkout-form\",\n})\n```\n\n## Styling guide\n\nEach part includes a `data-part` attribute you can target in CSS.\n\n### Focused state\n\nWhen the editable is in the focused mode, we set a `data-focus` attribute on the\n\"area\" part.\n\n```css\n[data-part=\"area\"][data-focus] {\n  /* CSS for the editable's focus state */\n}\n```\n\n### Empty state\n\nWhen the editable's value is empty, we set a `data-empty` attribute on the\n\"area\" part.\n\n```css\n[data-part=\"area\"][data-empty] {\n  /* CSS for the editable's focus state */\n}\n```\n\n### Disabled state\n\nWhen the editable is disabled, we set a `data-disabled` attribute on the \"area\"\npart.\n\n```css\n[data-part=\"area\"][data-disabled] {\n  /* CSS for the editable's focus state */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe editable machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; area: string; label: string; preview: string; input: string; control: string; submitTrigger: string; cancelTrigger: string; editTrigger: string; }>`\nDescription: The ids of the elements in the editable. Useful for composition.\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the input's value is invalid.\n\n**`name`**\nType: `string`\nDescription: The name attribute of the editable component. Used for form submission.\n\n**`form`**\nType: `string`\nDescription: The associate form of the underlying input.\n\n**`autoResize`**\nType: `boolean`\nDescription: Whether the editable should auto-resize to fit the content.\n\n**`activationMode`**\nType: `ActivationMode`\nDescription: The activation mode for the preview element.\n\n- \"focus\" - Enter edit mode when the preview is focused\n- \"dblclick\" - Enter edit mode when the preview is double-clicked\n- \"click\" - Enter edit mode when the preview is clicked\n- \"none\" - Edit can be triggered programmatically only\n\n**`submitMode`**\nType: `SubmitMode`\nDescription: The action that triggers submit in the edit mode:\n\n- \"enter\" - Trigger submit when the enter key is pressed\n- \"blur\" - Trigger submit when the editable is blurred\n- \"none\" - No action will trigger submit. You need to use the submit button\n- \"both\" - Pressing `Enter` and blurring the input will trigger submit\n\n**`selectOnFocus`**\nType: `boolean`\nDescription: Whether to select the text in the input when it is focused.\n\n**`edit`**\nType: `boolean`\nDescription: Whether the editable is in edit mode.\n\n**`defaultEdit`**\nType: `boolean`\nDescription: Whether the editable is in edit mode by default.\n\n**`onEditChange`**\nType: `(details: EditChangeDetails) => void`\nDescription: Function to call when the edit mode changes.\n\n**`maxLength`**\nType: `number`\nDescription: The maximum number of characters allowed in the editable\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the editable is disabled.\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the editable is read-only.\n\n**`required`**\nType: `boolean`\nDescription: Whether the editable is required.\n\n**`placeholder`**\nType: `string | { edit: string; preview: string; }`\nDescription: The placeholder text for the editable.\n\n**`translations`**\nType: `IntlTranslations`\nDescription: The translations for the editable.\n\n**`finalFocusEl`**\nType: `() => HTMLElement`\nDescription: The element to receive focus when the editable is closed.\n\n**`value`**\nType: `string`\nDescription: The controlled value of the editable.\n\n**`defaultValue`**\nType: `string`\nDescription: The initial value of the editable when rendered.\nUse when you don't need to control the value of the editable.\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function to call when the value changes.\n\n**`onValueRevert`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function to call when the value is reverted.\n\n**`onValueCommit`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function to call when the value is committed.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => Node | ShadowRoot | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n**`onPointerDownOutside`**\nType: `(event: PointerDownOutsideEvent) => void`\nDescription: Function called when the pointer is pressed down outside the component\n\n**`onFocusOutside`**\nType: `(event: FocusOutsideEvent) => void`\nDescription: Function called when the focus is moved outside the component\n\n**`onInteractOutside`**\nType: `(event: InteractOutsideEvent) => void`\nDescription: Function called when an interaction happens outside the component\n\n### Machine API\n\nThe editable `api` exposes the following methods:\n\n**`editing`**\nType: `boolean`\nDescription: Whether the editable is in edit mode\n\n**`empty`**\nType: `boolean`\nDescription: Whether the editable value is empty\n\n**`value`**\nType: `string`\nDescription: The current value of the editable\n\n**`valueText`**\nType: `string`\nDescription: The current value of the editable, or the placeholder if the value is empty\n\n**`setValue`**\nType: `(value: string) => void`\nDescription: Function to set the value of the editable\n\n**`clearValue`**\nType: `VoidFunction`\nDescription: Function to clear the value of the editable\n\n**`edit`**\nType: `VoidFunction`\nDescription: Function to enter edit mode\n\n**`cancel`**\nType: `VoidFunction`\nDescription: Function to exit edit mode, and discard any changes\n\n**`submit`**\nType: `VoidFunction`\nDescription: Function to exit edit mode, and submit any changes\n\n### Data Attributes\n\n**`Area`**\n\n**`data-scope`**: editable\n**`data-part`**: area\n**`data-focus`**: Present when focused\n**`data-disabled`**: Present when disabled\n**`data-placeholder-shown`**: Present when placeholder is shown\n\n**`Label`**\n\n**`data-scope`**: editable\n**`data-part`**: label\n**`data-focus`**: Present when focused\n**`data-invalid`**: Present when invalid\n**`data-required`**: Present when required\n\n**`Input`**\n\n**`data-scope`**: editable\n**`data-part`**: input\n**`data-disabled`**: Present when disabled\n**`data-readonly`**: Present when read-only\n**`data-invalid`**: Present when invalid\n**`data-autoresize`**: \n\n**`Preview`**\n\n**`data-scope`**: editable\n**`data-part`**: preview\n**`data-placeholder-shown`**: Present when placeholder is shown\n**`data-readonly`**: Present when read-only\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-autoresize`**: \n\n## Accessibility\n\n### Keyboard Interactions\n\n**`Enter`**\nDescription: Saves the edited content and exits edit mode.\n\n**`Escape`**\nDescription: Discards the changes and exits edit mode.","package":"@zag-js/editable","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/editable.mdx"}