# Inline Edit

Text that turns into a field when clicked, for changing a value where it's shown.

Source: [inline-edit.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/inline-edit.tsx)

```tsx
"use client";

import * as React from "react";

import {
  DescriptionList,
  DescriptionListDetails,
  DescriptionListItem,
  DescriptionListTerm,
} from "@/components/ui/description-list";
import {
  InlineEdit,
  InlineEditError,
  InlineEditInput,
  InlineEditPreview,
  InlineEditTextarea,
} from "@/components/ui/inline-edit";

const fields = [
  { key: "name", label: "Name", required: true },
  { key: "title", label: "Title" },
  { key: "email", label: "Email", required: true },
  { key: "notes", label: "Notes", multiline: true },
] as const;

type Contact = Record<(typeof fields)[number]["key"], string>;

export default function InlineEditDescriptionList() {
  const id = React.useId();
  const [contact, setContact] = React.useState<Contact>({
    name: "Sam Rivera",
    title: "Head of Design",
    email: "sam@example.com",
    notes: "",
  });

  return (
    <DescriptionList divided className="max-w-md">
      {fields.map((field) => (
        <DescriptionListItem key={field.key} className="items-start">
          <DescriptionListTerm id={`${id}-${field.key}`} className="py-1">
            {field.label}
          </DescriptionListTerm>
          <DescriptionListDetails>
            <InlineEdit
              value={contact[field.key]}
              onValueChange={(value) =>
                setContact((current) => ({ ...current, [field.key]: value }))
              }
              validate={(value) =>
                "required" in field && !value.trim()
                  ? `Enter a ${field.label.toLowerCase()}.`
                  : null
              }
              placeholder={`Add ${field.label.toLowerCase()}`}
              aria-labelledby={`${id}-${field.key}`}
            >
              <InlineEditPreview />
              {"multiline" in field ? <InlineEditTextarea /> : <InlineEditInput />}
              <InlineEditError />
            </InlineEdit>
          </DescriptionListDetails>
        </DescriptionListItem>
      ))}
    </DescriptionList>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/inline-edit
```

## Usage

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";

export function DisplayName() {
  return (
    <Field>
      <FieldLabel>Display name</FieldLabel>
      <InlineEdit defaultValue="Ada Park" onValueChange={(name) => console.log(name)}>
        <InlineEditPreview />
        <InlineEditInput />
      </InlineEdit>
    </Field>
  );
}
```

`InlineEditPreview` shows the value as text, with an underline and a pencil when it's hovered and a
pencil when it's focused, and a click swaps it for `InlineEditInput` in the same place, at the same size, with the value
selected. The input is an [Input](https://ui.jarv.is/components/input.md) with `variant="underline"`, which has no
padding at the sides, so the text lines up with the label whether it's being edited or not. Enter
saves the edit and Escape puts the value back, and both return focus to the preview. Moving focus
anywhere else saves it too. `onValueChange` is called only when a saved value differs from the last
one, and `value` makes it controlled.

A `FieldLabel` in the same [Field](https://ui.jarv.is/components/field.md) names the preview and the input, and clicking
it starts an edit. Without one, give `InlineEdit` an `aria-label`, or point its `aria-labelledby` at
the element that names it, such as a `<dt>`.

## Composition

- `InlineEdit`
  - `InlineEditPreview`
  - `InlineEditTrigger`
  - `InlineEditInput`
  - `InlineEditTextarea`
  - `InlineEditControls`
    - `InlineEditSubmit`
    - `InlineEditCancel`
  - `InlineEditError`

## Examples

### Basic

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";

export default function InlineEditBasic() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Display name</FieldLabel>
      <InlineEdit defaultValue="Ada Park" placeholder="Add a name">
        <InlineEditPreview />
        <InlineEditInput />
      </InlineEdit>
    </Field>
  );
}
```

### Multiple lines

`InlineEditTextarea` edits text over several lines, growing with it, in a
[Textarea](https://ui.jarv.is/components/textarea.md) with `variant="underline"`. Enter starts a new line, so
⌘Enter, or Ctrl+Enter, saves. The preview keeps the line breaks.

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { InlineEdit, InlineEditPreview, InlineEditTextarea } from "@/components/ui/inline-edit";

export default function InlineEditMultiline() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Description</FieldLabel>
      <InlineEdit
        defaultValue={"Marketing site for the spring launch.\nOwned by the web team."}
        placeholder="Add a description"
      >
        <InlineEditPreview />
        <InlineEditTextarea />
      </InlineEdit>
    </Field>
  );
}
```

### Save and cancel buttons

`InlineEditControls` adds buttons to save and cancel at the end of the field, each with a tooltip naming
its shortcut. Pass it `InlineEditSubmit` and `InlineEditCancel` to change their order or icons.
Here `blurAction="none"` keeps the field open when focus leaves it, so only Enter or a button
saves, and `"cancel"` would put the value back.

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import {
  InlineEdit,
  InlineEditControls,
  InlineEditInput,
  InlineEditPreview,
} from "@/components/ui/inline-edit";

export default function InlineEditWithControls() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Project name</FieldLabel>
      <InlineEdit defaultValue="Website redesign" blurAction="none">
        <InlineEditPreview />
        <InlineEditInput />
        <InlineEditControls />
      </InlineEdit>
    </Field>
  );
}
```

### Validation

`validate` checks the value before it's saved. Return a message to keep the field open, and an
`InlineEditError` shows it and is announced. It clears as soon as the value changes.

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import {
  InlineEdit,
  InlineEditError,
  InlineEditInput,
  InlineEditPreview,
} from "@/components/ui/inline-edit";

const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function validateEmail(email: string) {
  if (!email.trim()) return "Enter an email address.";
  if (!emailPattern.test(email)) return "Enter an address like name@example.com.";
}

export default function InlineEditValidation() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Billing email</FieldLabel>
      <InlineEdit defaultValue="billing@example.com" validate={validateEmail}>
        <InlineEditPreview />
        <InlineEditInput type="email" />
        <InlineEditError />
      </InlineEdit>
    </Field>
  );
}
```

### Saving

When `onValueChange` returns a promise, the field stays open and read-only, with a spinner, until
the promise settles, and the save button shows the spinner in its place. If it rejects, the field
stays open with the error's message in the `InlineEditError`, to try again or press Escape.

```tsx
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
import {
  InlineEdit,
  InlineEditError,
  InlineEditInput,
  InlineEditPreview,
} from "@/components/ui/inline-edit";

const taken = new Set(["admin", "ada", "support"]);

async function saveUsername(username: string) {
  await new Promise((resolve) => setTimeout(resolve, 1000));
  if (taken.has(username.toLowerCase())) {
    throw new Error(`“${username}” is taken. Try another.`);
  }
}

export default function InlineEditSaving() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Username</FieldLabel>
      <InlineEdit defaultValue="ada-park" onValueChange={saveUsername}>
        <InlineEditPreview />
        <InlineEditInput />
        <InlineEditError />
      </InlineEdit>
      <FieldDescription>“admin” is taken, to show a failed save.</FieldDescription>
    </Field>
  );
}
```

### Headings

The preview and the field take the font of the `InlineEdit`, so a larger or bolder one makes an
editable heading. The field is never smaller than 16 pixels on phones, so Safari doesn't zoom into
it.

```tsx
import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";

export default function InlineEditHeading() {
  return (
    <InlineEdit
      defaultValue="Q3 planning"
      placeholder="Untitled"
      aria-label="Page title"
      className="max-w-md text-xl font-semibold"
    >
      <InlineEditPreview />
      <InlineEditInput />
    </InlineEdit>
  );
}
```

### Edit button

Set `activationMode="none"` when the preview holds something clickable of its own, such as a link,
and add an `InlineEditTrigger`, a button with a pencil, to start an edit. A function as the
preview's child renders the value.

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import {
  InlineEdit,
  InlineEditControls,
  InlineEditInput,
  InlineEditPreview,
  InlineEditTrigger,
} from "@/components/ui/inline-edit";

export default function InlineEditWithTrigger() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Website</FieldLabel>
      <InlineEdit defaultValue="https://example.com" activationMode="none">
        <InlineEditPreview>
          {(url) => (
            <a
              href={url}
              target="_blank"
              rel="noreferrer"
              className="rounded-xs underline decoration-border-strong decoration-[0.5px] underline-offset-2 hover:decoration-current focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
            >
              {url.replace(/^https?:\/\//, "")}
            </a>
          )}
        </InlineEditPreview>
        <InlineEditTrigger />
        <InlineEditInput type="url" />
        <InlineEditControls />
      </InlineEdit>
    </Field>
  );
}
```

### Double-click

`activationMode="double-click"` starts an edit on a double click, or on Enter or Space from the
keyboard, so a single click is free for something else, such as selecting a row. `selectOnFocus`
set to false puts the caret at the end of the value in place of selecting it.

```tsx
"use client";

import * as React from "react";

import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

const initialFiles = [
  { id: "brief", name: "Launch brief.pdf", size: "2.4 MB" },
  { id: "logo", name: "Logo final v3.svg", size: "18 KB" },
  { id: "budget", name: "Budget 2027.xlsx", size: "96 KB" },
];

export default function InlineEditDoubleClick() {
  const [files, setFiles] = React.useState(initialFiles);

  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHead>Name</TableHead>
          <TableHead className="text-right">Size</TableHead>
        </TableRow>
      </TableHeader>
      <TableBody>
        {files.map((file) => (
          <TableRow key={file.id}>
            <TableCell className="py-1 pl-1 font-medium">
              <InlineEdit
                value={file.name}
                onValueChange={(name) =>
                  setFiles((current) =>
                    current.map((item) => (item.id === file.id ? { ...item, name } : item)),
                  )
                }
                validate={(name) => (name.trim() ? null : "Enter a name.")}
                activationMode="double-click"
                selectOnFocus={false}
                aria-label="File name"
              >
                <InlineEditPreview />
                <InlineEditInput />
              </InlineEdit>
            </TableCell>
            <TableCell className="text-right text-muted-foreground">{file.size}</TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
```

### In a description list

In the example at the top, each value of a [Description List](https://ui.jarv.is/components/description-list.md) is an
`InlineEdit` named by its `<dt>`, which takes the same vertical padding as the preview so the two
line up.

### Disabled

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";

export default function InlineEditDisabled() {
  return (
    <Field disabled className="max-w-sm">
      <FieldLabel>Workspace</FieldLabel>
      <InlineEdit defaultValue="Acme">
        <InlineEditPreview />
        <InlineEditInput />
      </InlineEdit>
    </Field>
  );
}
```

### Read-only

A read-only preview isn't a button, and shows no pencil.

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";

export default function InlineEditReadOnly() {
  return (
    <Field className="max-w-sm">
      <FieldLabel>Account ID</FieldLabel>
      <InlineEdit defaultValue="acct_8f2k19" readOnly>
        <InlineEditPreview className="font-mono" />
        <InlineEditInput />
      </InlineEdit>
    </Field>
  );
}
```

### In a form

Give a `Field` or the `InlineEdit` a `name`, and the saved value submits under it, or pass `form`
with a form's `id` when it sits outside it. A `Form`'s `onFormSubmit` gets it too. An edit still
open when the form submits isn't included until it's saved, and a form reset brings back
`defaultValue`. A controlled `InlineEdit` leaves resets to its parent: a reset still closes an open
edit, but `value` stays until the parent changes it, such as from the form's `onReset`. `required`
stops the form submitting without a saved value. In a `Field`, a `FieldLabel` names the preview and
the input, they read out the field's `FieldDescription`, and an invalid field marks them invalid.

```tsx
import { Button } from "@/components/ui/button";
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
import { Form } from "@/components/ui/form";
import { InlineEdit, InlineEditInput, InlineEditPreview } from "@/components/ui/inline-edit";
import { toast } from "@/components/ui/toast";

export default function InlineEditForm() {
  return (
    <Form
      className="max-w-sm"
      onFormSubmit={(values) =>
        toast.add({ title: "Profile saved", description: `Shown as ${values.displayName}.` })
      }
    >
      <Field name="displayName">
        <FieldLabel>Display name</FieldLabel>
        <InlineEdit defaultValue="Ada Park" placeholder="Add a name">
          <InlineEditPreview />
          <InlineEditInput />
        </InlineEdit>
        <FieldDescription>Shown on your profile and beside your comments.</FieldDescription>
      </Field>
      <Button type="submit" variant="primary" className="self-start">
        Save profile
      </Button>
    </Form>
  );
}
```

## Props

| Part | Extends |
| --- | --- |
| `InlineEdit` | `<div>` |
| `InlineEditPreview` | `<button>` |
| `InlineEditTrigger` | [`Button`](https://ui.jarv.is/components/button.md) |
| `InlineEditInput` | [`Input`](https://ui.jarv.is/components/input.md) |
| `InlineEditTextarea` | [`Textarea`](https://ui.jarv.is/components/textarea.md) |
| `InlineEditControls` | `<div>` |
| `InlineEditSubmit` | [`Button`](https://ui.jarv.is/components/button.md) |
| `InlineEditCancel` | [`Button`](https://ui.jarv.is/components/button.md) |
| `InlineEditError` | `<p>` |

Base UI's [styling](https://base-ui.com/react/handbook/styling) and [composition](https://base-ui.com/react/handbook/composition) guides say more about `className`, `style` and `render`.

### InlineEdit

| Prop               | Type                                                     | Description                                                                                                                                                                                      |
| ------------------ | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `value?`           | `string`                                                 | The saved value. A form reset doesn't change it, since resetting doesn't call `onValueChange`, so reset it from the form's `onReset` when it should go back too.                                 |
| `defaultValue?`    | `string`                                                 | Default: `""`                                                                                                                                                                                    |
| `onValueChange?`   | `((value: string) => void \| Promise<unknown>)`          | Called with the new value when an edit is saved. Return a promise to keep the editor open, showing it's saving, until it settles. If it rejects, the editor stays open with the error's message. |
| `editing?`         | `boolean`                                                | Whether the editor is open.                                                                                                                                                                      |
| `defaultEditing?`  | `boolean`                                                | Default: `false`                                                                                                                                                                                 |
| `onEditingChange?` | `((editing: boolean) => void)`                           |                                                                                                                                                                                                  |
| `validate?`        | `((value: string) => string \| null \| undefined)`       | Checks a value before it's saved. Return a message to keep the editor open and show it.                                                                                                          |
| `activationMode?`  | `"click" \| "double-click" \| "none"`                    | What a click on the preview does: `click` and `double-click` open the editor, and `none` leaves it to an `InlineEditTrigger`. Default: `"click"`                                                 |
| `blurAction?`      | `"cancel" \| "none" \| "save"`                           | What happens to a changed value when focus leaves the editor. Default: `"save"`                                                                                                                  |
| `selectOnFocus?`   | `boolean`                                                | Selects the whole value when the editor opens, rather than putting the caret at its end. Default: `true`                                                                                         |
| `placeholder?`     | `string`                                                 | Shown in the preview while there's no value, and in the editor while it's empty.                                                                                                                 |
| `disabled?`        | `boolean`                                                | Default: `false`                                                                                                                                                                                 |
| `readOnly?`        | `boolean`                                                | Default: `false`                                                                                                                                                                                 |
| `required?`        | `boolean`                                                | Whether a form can't be submitted without a saved value. An edit still open doesn't count. Default: `false`                                                                                      |
| `name?`            | `string`                                                 | Submits the saved value with a form under this name.                                                                                                                                             |
| `form?`            | `string`                                                 | The `id` of the form to submit the value with, when it isn't inside it.                                                                                                                          |
| `aria-label?`      | `string`                                                 | Names the preview and the editor when there's no `FieldLabel`.                                                                                                                                   |
| `aria-labelledby?` | `string`                                                 | Names the preview and the editor by the `id` of an element, such as a `<dt>`.                                                                                                                    |
| `className?`       | `string`                                                 | CSS class applied to the element.                                                                                                                                                                |
| `style?`           | `React.CSSProperties`                                    | Style applied to the element.                                                                                                                                                                    |
| `render?`          | `ComponentRenderFn<HTMLProps, {}> \| React.ReactElement` | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.        |

### InlineEditPreview

| Prop         | Type                                                      | Description                                                                                                                                                                               |
| ------------ | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children?`  | `((value: string) => React.ReactNode) \| React.ReactNode` | Renders the value, such as to format it or add an icon. Without it, the value shows as text.                                                                                              |
| `className?` | `string`                                                  | CSS class applied to the element.                                                                                                                                                         |
| `style?`     | `React.CSSProperties`                                     | Style applied to the element.                                                                                                                                                             |
| `render?`    | `ComponentRenderFn<HTMLProps, {}> \| React.ReactElement`  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### InlineEditTrigger

| Prop                     | Type                                                                                 | Description                                                                                                                                                                                       |
| ------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `focusableWhenDisabled?` | `boolean`                                                                            | Whether the button should be focusable when disabled. Default: `false`                                                                                                                            |
| `nativeButton?`          | `boolean`                                                                            | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). Default: `true` |
| `variant?`               | `"destructive" \| "ghost" \| "link" \| "outline" \| "primary" \| "secondary"`        | Default: `"ghost"`                                                                                                                                                                                |
| `size?`                  | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs"` | Default: `"icon"`                                                                                                                                                                                 |
| `className?`             | `string \| ((state: ButtonState) => string \| undefined)`                            | CSS class applied to the element, or a function that returns a class based on the component's state.                                                                                              |
| `style?`                 | `React.CSSProperties \| ((state: ButtonState) => React.CSSProperties \| undefined)`  | Style applied to the element, or a function that returns a style object based on the component's state.                                                                                           |
| `render?`                | `ComponentRenderFn<HTMLProps, ButtonState> \| React.ReactElement`                    | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.         |

### InlineEditInput

| Prop         | Type                        | Description                                                            |
| ------------ | --------------------------- | ---------------------------------------------------------------------- |
| `size?`      | `"default" \| "lg" \| "sm"` | The height: `sm` 24px, `default` 28px, `lg` 32px. Default: `"default"` |
| `className?` | `string`                    | CSS class applied to the element.                                      |
| `style?`     | `React.CSSProperties`       | Style applied to the element.                                          |

### InlineEditTextarea

| Prop         | Type                  | Description                       |
| ------------ | --------------------- | --------------------------------- |
| `className?` | `string`              | CSS class applied to the element. |
| `style?`     | `React.CSSProperties` | Style applied to the element.     |

### InlineEditControls

| Prop         | Type                                                     | Description                                                                                                                                                                               |
| ------------ | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className?` | `string`                                                 | CSS class applied to the element.                                                                                                                                                         |
| `style?`     | `React.CSSProperties`                                    | Style applied to the element.                                                                                                                                                             |
| `render?`    | `ComponentRenderFn<HTMLProps, {}> \| React.ReactElement` | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### InlineEditSubmit

| Prop                     | Type                                                                                 | Description                                                                                                                                                                                       |
| ------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `focusableWhenDisabled?` | `boolean`                                                                            | Whether the button should be focusable when disabled. Default: `false`                                                                                                                            |
| `nativeButton?`          | `boolean`                                                                            | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). Default: `true` |
| `variant?`               | `"destructive" \| "ghost" \| "link" \| "outline" \| "primary" \| "secondary"`        | Default: `"ghost"`                                                                                                                                                                                |
| `size?`                  | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs"` | Default: `"icon-xs"`                                                                                                                                                                              |
| `className?`             | `string \| ((state: ButtonState) => string \| undefined)`                            | CSS class applied to the element, or a function that returns a class based on the component's state.                                                                                              |
| `style?`                 | `React.CSSProperties \| ((state: ButtonState) => React.CSSProperties \| undefined)`  | Style applied to the element, or a function that returns a style object based on the component's state.                                                                                           |
| `render?`                | `ComponentRenderFn<HTMLProps, ButtonState> \| React.ReactElement`                    | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.         |

### InlineEditCancel

| Prop                     | Type                                                                                 | Description                                                                                                                                                                                       |
| ------------------------ | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `focusableWhenDisabled?` | `boolean`                                                                            | Whether the button should be focusable when disabled. Default: `false`                                                                                                                            |
| `nativeButton?`          | `boolean`                                                                            | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). Default: `true` |
| `variant?`               | `"destructive" \| "ghost" \| "link" \| "outline" \| "primary" \| "secondary"`        | Default: `"ghost"`                                                                                                                                                                                |
| `size?`                  | `"default" \| "icon" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "lg" \| "sm" \| "xs"` | Default: `"icon-xs"`                                                                                                                                                                              |
| `className?`             | `string \| ((state: ButtonState) => string \| undefined)`                            | CSS class applied to the element, or a function that returns a class based on the component's state.                                                                                              |
| `style?`                 | `React.CSSProperties \| ((state: ButtonState) => React.CSSProperties \| undefined)`  | Style applied to the element, or a function that returns a style object based on the component's state.                                                                                           |
| `render?`                | `ComponentRenderFn<HTMLProps, ButtonState> \| React.ReactElement`                    | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.         |

### InlineEditError

| Prop         | Type                                                     | Description                                                                                                                                                                               |
| ------------ | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className?` | `string`                                                 | CSS class applied to the element.                                                                                                                                                         |
| `style?`     | `React.CSSProperties`                                    | Style applied to the element.                                                                                                                                                             |
| `render?`    | `ComponentRenderFn<HTMLProps, {}> \| React.ReactElement` | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
