Skip to content

InlineEditable API

Complete type definitions and prop reference for the InlineEditable component ecosystem.


InlineEditable (Headless)

Headless component for inline editing with full render control.

Import

tsx
import { InlineEditable, type InlineEditableProps, type InlineEditableDisplayProps, type InlineEditableEditorProps } from '@/shared/ui/components/editable/InlineEditable'

Props

InlineEditableProps<T>

PropTypeDefaultDescription
valueTRequiredCurrent value to display/edit
onSave(value: T) => Promise<void> | voidRequiredSave function (sync or async)
renderDisplay(props: InlineEditableDisplayProps<T>) => ReactNodeRequiredRender function for display mode
renderEditor(props: InlineEditableEditorProps<T>) => ReactNodeRequiredRender function for edit mode
onCancel() => void-Called when editing is cancelled
onSaveSuccess() => void-Called when save succeeds
onSaveError(error: Error) => void-Called when save fails
enableKeyboardShortcutsbooleantrueEnable Enter/Escape shortcuts
classNamestring-Additional CSS classes for wrapper

InlineEditableDisplayProps<T>

Props passed to renderDisplay function:

PropertyTypeDescription
valueTCurrent value
onStartEdit() => voidFunction to enter edit mode

InlineEditableEditorProps<T>

Props passed to renderEditor function:

PropertyTypeDescription
valueTCurrent draft value
onChange(value: T) => voidUpdate draft value
onSave() => voidSave current draft
onCancel() => voidCancel editing
isSavingbooleanWhether save is in progress
errorError | nullError from save attempt
inputProps{ onKeyDown: (e: KeyboardEvent) => void }Props to spread on input for keyboard handling

Example

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

function EditableName({ name, onUpdate }) {
  return (
    <InlineEditable
      value={name}
      onSave={async (newName) => {
        await api.updateName(newName)
      }}
      onSaveSuccess={() => toast.success('Name updated')}
      onSaveError={(error) => toast.error(error.message)}
      renderDisplay={({ value, onStartEdit }) => (
        <button
          onClick={onStartEdit}
          className="hover:underline cursor-pointer"
        >
          {value}
        </button>
      )}
      renderEditor={({ value, onChange, onSave, onCancel, isSaving, error, inputProps }) => (
        <div className="flex gap-2">
          <input
            value={value}
            onChange={(e) => onChange(e.target.value)}
            {...inputProps}
            autoFocus
            disabled={isSaving}
          />
          <button onClick={onSave} disabled={isSaving}>
            {isSaving ? 'Saving...' : 'Save'}
          </button>
          <button onClick={onCancel} disabled={isSaving}>
            Cancel
          </button>
          {error && <p className="text-destructive">{error.message}</p>}
        </div>
      )}
    />
  )
}

EditableTrigger

Reusable trigger button for entering edit mode.

Import

tsx
import { EditableTrigger, type EditableTriggerProps } from '@/shared/ui/components/editable/EditableTrigger'

Props

EditableTriggerProps

PropTypeDefaultDescription
onClick() => voidRequiredCallback when trigger is clicked
childrenReactNodeRequiredContent to display
showEditIconbooleantrueShow edit icon on hover
classNamestring-Additional CSS classes
disabledbooleanfalseWhether trigger is disabled

Features

  • Ghost button styling (looks like inline content)
  • Edit icon appears on hover
  • Keyboard accessible (Enter/Space)
  • Focus ring for accessibility

Example

tsx
import { EditableTrigger } from '@/shared/ui/components/editable/EditableTrigger'

<EditableTrigger onClick={() => startEdit()}>
  <span className="font-medium">{userName}</span>
</EditableTrigger>

// Without edit icon
<EditableTrigger onClick={() => startEdit()} showEditIcon={false}>
  <h1 className="text-2xl">{title}</h1>
</EditableTrigger>

// Disabled state
<EditableTrigger onClick={() => startEdit()} disabled={!canEdit}>
  <span>{value}</span>
</EditableTrigger>

EditableActions

Reusable save/cancel action buttons.

Import

tsx
import { EditableActions, type EditableActionsProps } from '@/shared/ui/components/editable/EditableActions'

Props

EditableActionsProps

PropTypeDefaultDescription
onSave() => voidRequiredCallback when save is clicked
onCancel() => voidRequiredCallback when cancel is clicked
isSavingbooleanfalseWhether save operation is in progress
disabledbooleanfalseWhether actions are disabled
size'default' | 'sm' | 'icon''default'Button size variant
classNamestring-Additional CSS classes for container

Features

  • Check (✓) icon for save
  • X icon for cancel
  • Consistent styling across app
  • Loading state support
  • Size variants

Example

tsx
import { EditableActions } from '@/shared/ui/components/editable/EditableActions'

// Default size
<EditableActions
  onSave={handleSave}
  onCancel={handleCancel}
/>

// Small size
<EditableActions
  onSave={handleSave}
  onCancel={handleCancel}
  size="sm"
/>

// Icon size (for compact UIs)
<EditableActions
  onSave={handleSave}
  onCancel={handleCancel}
  size="icon"
/>

// With loading state
<EditableActions
  onSave={handleSave}
  onCancel={handleCancel}
  isSaving={isSaving}
/>

InlineEditableField

Complete form field implementation with label, error display, and more.

Import

tsx
import { InlineEditableField, type InlineEditableFieldProps } from '@/shared/ui/components/editable/InlineEditableField'

Props

InlineEditableFieldProps

PropTypeDefaultDescription
Core
idstringRequiredUnique field identifier
valuestringRequiredCurrent value
onSave(value: string) => Promise<void> | voidRequiredSave function
Display
labelReactNode-Field label
placeholderstring-Input placeholder
typestring'text'Input type
State
disabledbooleanfalseWhether field is disabled
readOnlybooleanfalseWhether field is read-only
isSavingboolean-External saving state (controlled)
Callbacks
onCancel() => void-Called when editing is cancelled
onBlur() => void-Custom blur handler
Customization
renderDisplay(startEdit: () => void) => ReactNode-Custom display render
renderInput(props: InputProps) => ReactNode-Custom input render
Styling
classNamestring-Container CSS classes
displayClassNamestring-Display mode CSS classes
inputClassNamestring-Input CSS classes
Features
editedbooleanfalseMark field as edited
showEditedBadgebooleantrueShow "edited" badge
editedBadgeContentReactNode'Edited'Badge content
saveOnBlurbooleanfalseSave when input loses focus
hideActionsbooleanfalseHide save/cancel buttons
errorstring-Error message to display

InputProps (for renderInput)

ts
interface InputProps {
  value: string
  onChange: (value: string) => void
  onKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void
  onBlur: () => void
  autoFocus: boolean
  isSaving: boolean
}

Examples

Basic Usage

tsx
<InlineEditableField
  id="user-name"
  label="Name"
  value={user.name}
  onSave={async (value) => await updateUser({ name: value })}
  placeholder="Enter name..."
/>

With Edited Badge

tsx
<InlineEditableField
  id="user-email"
  label="Email"
  value={user.email}
  onSave={handleSave}
  edited={user.email !== originalEmail}
  showEditedBadge
  editedBadgeContent="Modified"
/>

Save on Blur

tsx
<InlineEditableField
  id="quick-edit"
  value={value}
  onSave={handleSave}
  saveOnBlur
  hideActions
  placeholder="Click to edit..."
/>

Custom Display

tsx
<InlineEditableField
  id="custom-display"
  value={user.name}
  onSave={handleSave}
  renderDisplay={(startEdit) => (
    <div className="flex items-center gap-2" onClick={startEdit}>
      <Avatar src={user.avatar} />
      <span className="font-bold">{user.name}</span>
    </div>
  )}
/>

Custom Input (Textarea)

tsx
<InlineEditableField
  id="description"
  label="Description"
  value={description}
  onSave={handleSave}
  renderInput={({ value, onChange, onKeyDown, autoFocus }) => (
    <Textarea
      value={value}
      onChange={(e) => onChange(e.target.value)}
      onKeyDown={onKeyDown}
      autoFocus={autoFocus}
      rows={4}
    />
  )}
/>

With Error Handling

tsx
<InlineEditableField
  id="validated-field"
  value={value}
  onSave={async (value) => {
    if (!value.trim()) throw new Error('Value required')
    await api.update(value)
  }}
  error={validationError}
/>

Type Reference

Common Types

ts
// Generic value type
type InlineEditableValue = string | number | boolean | any

// Save function
type SaveFunction<T> = (value: T) => Promise<void> | void

// Lifecycle callbacks
type OnCancel = () => void
type OnSaveSuccess = () => void
type OnSaveError = (error: Error) => void

// Keyboard event handler
type KeyboardHandler = (e: React.KeyboardEvent) => void

Composition Patterns

Building Custom Editables

Combine primitives for custom implementations:

tsx
import { InlineEditable } from '@/shared/ui/components/editable/InlineEditable'
import { EditableTrigger } from '@/shared/ui/components/editable/EditableTrigger'
import { EditableActions } from '@/shared/ui/components/editable/EditableActions'

function CustomEditable({ value, onSave }) {
  return (
    <InlineEditable
      value={value}
      onSave={onSave}
      renderDisplay={({ value, onStartEdit }) => (
        <EditableTrigger onClick={onStartEdit}>
          <YourCustomDisplay value={value} />
        </EditableTrigger>
      )}
      renderEditor={({ value, onChange, onSave, onCancel, isSaving, inputProps }) => (
        <div className="flex items-center gap-2">
          <YourCustomInput
            value={value}
            onChange={onChange}
            {...inputProps}
          />
          <EditableActions
            onSave={onSave}
            onCancel={onCancel}
            isSaving={isSaving}
          />
        </div>
      )}
    />
  )
}

See Also