# Inline edit (goodtabs UI)

Text that becomes an input on click. Enter saves, Escape cancels, blur saves.

## Install

```bash
npx shadcn@latest add https://ui.goodtabs.co/r/inline-edit.json
```

Or, with the @goodtabs namespace configured in components.json: `npx shadcn@latest add @goodtabs/inline-edit`

## Usage

```tsx
import { InlineEdit } from "@/components/ui/inline-edit"

const [title, setTitle] = useState("Website redesign")

<InlineEdit as="h2" value={title} onSave={setTitle} label="Project title" className="text-2xl font-medium tracking-tight" />
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| value | `string` |  | The current text. Controlled. |
| onSave | `(value: string) => void` |  | Called with the trimmed text when it changed. |
| placeholder | `string` | `"Empty"` | Shown while the value is empty. |
| label | `string` |  | Accessible name for the field, for example "Project title". |
| as | `"span" | "p" | "div" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6"` | `"span"` | The element around the text. |

## When to use

- Renaming a project, document, column or label in place
- Titles and short one-line fields that are edited rarely and read often
- Anywhere a full form would be heavier than the change

## When not to use

- For multi-line text; use a textarea in a form
- When the value needs validation or a save button; use labeled-field in a form
- For values that change often, where a plain Input is clearer

## Notes for agents

- It is controlled: keep value in state and set it in onSave. onSave is a function, so render it from a client component
- onSave only fires when the trimmed text differs from value; Escape restores the old text
- Put font size and weight on className; the display button and the input both inherit them, so the swap does not shift the layout
- The display is a real button inside the as element, so a heading stays a heading for screen readers

Docs: https://ui.goodtabs.co/components/inline-edit