Skip to content

InlineEditable

Purpose

InlineEditable is a thin, headless UI wrapper over useInlineEdit.

It provides a consistent inline edit lifecycle:

  • Display mode → start editing
  • Edit mode → draft value
  • Save / cancel
  • Save loading + error state
  • Optional Enter/Escape keyboard shortcuts

When to use

Use InlineEditable when:

  • You’re editing a single value inline (rename, quick field edit)
  • You want a consistent save/cancel lifecycle but custom markup
  • Render-props are a good fit for your component structure

Prefer the hook (useInlineEdit) when:

  • You need total control over state and rendering
  • You’re editing multiple fields at once (inline “form”)
  • You need to integrate with custom focus management

Example

tsx
import React from 'react'
import { InlineEditable } from '@/shared/ui/components/InlineEditable'

export function EditableName({ name }: { name: string }) {
  return (
    <InlineEditable
      value={name}
      onSave={async (next) => {
        // await repository.updateName(next)
      }}
      renderDisplay={({ value, onStartEdit }) => (
        <button type="button" onClick={onStartEdit}>
          {value}
        </button>
      )}
      renderEditor={({ value, onChange, onSave, onCancel, isSaving, inputProps, error }) => (
        <div>
          <input
            value={value as string}
            onChange={(e) => onChange(e.target.value)}
            {...inputProps}
            autoFocus
          />

          <button type="button" onClick={onSave} disabled={isSaving}>
            {isSaving ? 'Saving…' : 'Save'}
          </button>
          <button type="button" onClick={onCancel} disabled={isSaving}>
            Cancel
          </button>

          {error && <p role="alert">Save failed.</p>}
        </div>
      )}
    />
  )
}

Props (concise)

  • value
    • The external value to display when not editing.
  • onSave(value)
    • Called when saving the draft. May be sync or async.
  • renderDisplay({ value, onStartEdit })
    • Render display mode; call onStartEdit() to enter edit mode.
  • renderEditor({ value, onChange, onSave, onCancel, isSaving, error, inputProps })
    • Render edit mode; use inputProps.onKeyDown for Enter/Escape handling.
  • enableKeyboardShortcuts
    • Defaults to true. Enables Enter-to-save / Escape-to-cancel behavior (via the underlying hook).
  • onCancel / onSaveSuccess / onSaveError
    • Lifecycle callbacks forwarded to useInlineEdit.
  • className
    • Optional wrapper class.

Notes / Gotchas

  • External updates: when value changes while not editing, InlineEditable syncs originalValue so display stays up to date.
  • Draft typing: for non-string values, pass a typed InlineEditable<T> and handle conversion in your editor.
  • Keyboard handling: for custom key behavior, you can ignore inputProps and handle key events yourself, but then you own save/cancel UX consistency.