InlineEditor
Click-to-edit text with async save, error display, optional input masks, and native input types.
InlineEditor is a client component that shows a value as text and switches to an editable field with save and cancel buttons. Use it for click-to-edit fields on detail pages. It uses react-textarea-autosize for multi-line editing and MaskedField when a mask preset is set.
Import#
import { InlineEditor } from "@reactberry/system/blocks";
Usage#
"use client";
import { InlineEditor } from "@reactberry/system/blocks";
import { Box } from "@reactberry/system/elements";
async function saveField(id: string, text: string) {
const res = await fetch(`/api/projects/${id}`, {
method: "PATCH",
body: JSON.stringify({ description: text }),
});
if (!res.ok) throw new Error("Could not save the description.");
}
export default function ProjectDescription({ project }: { project: { id: string; description: string | null } }) {
return (
<Box maxWidth="32rem">
<InlineEditor
id={project.id}
label="Description"
initialText={project.description}
placeholder="Add a description"
onSave={saveField}
/>
</Box>
);
}
Examples#
Masked phone number#
Set maskPreset to a key of maskPresets. The read-only text is formatted with the same mask.
Native input and custom buttons#
inputType renders a single-line input, and buttonConfig sets props such as variant and aria-label on each button.
API#
InlineEditor#
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | number | — | Passed to |
initialText | string | null | — | Current stored value. |
placeholder | string | — | Field placeholder, also shown in italics when there is no value. |
onSave* | (id: string, text: string) => Promise<void> | — | Called on save. Resolving exits edit mode; rejecting keeps the editor
open and shows the error's |
label | ReactNode | — | Header label. |
editLabel | string | — | Text shown next to the pencil icon on the edit button. |
isDisabled | boolean | false | Disables the edit button, the read-only click target and the field. |
fieldProps | Record<string, any> | {} | Spread onto the active field after its defaults. |
containerProps | Record<string, any> | {} | Spread onto the outer |
headerProps | Record<string, any> | {} | Spread onto the header |
controlSize | string | small |
|
buttonConfig | any | {… | Props spread onto each button, keyed |
maskPreset | string | — | A key of |
inputType | string | — | Native input type (e.g. "email", "url", "tel"). Renders a single-line
input instead of the autosizing textarea, so the browser applies its own
keyboard and validation. Ignored when |
displayText | string | null | — | Text shown when not editing, for fields whose stored value is not the
human-readable form (e.g. a date input's |
The default buttonConfig sets variant: "ghost" for edit, variant: "success" for save and variant: "default" for cancel. No other props are accepted.
Keyboard#
| Key | Action |
|---|---|
EnterorSpace | Opens the editor when the read-only value is focused. |
Accessibility#
- In read-only mode the value area has
role="button"andtabIndexof0. Both are removed whenisDisabledis set. - Error messages render with
role="alert"and are linked to the field througharia-describedby. - Save and cancel buttons contain only icons and have no
aria-label; pass one throughbuttonConfig.saveandbuttonConfig.cancel.
Notes#
- Error messages use the thrown
Error'smessage, or"Something went wrong. Please try again.". Errors are also logged withconsole.error. - Passing
buttonConfigreplaces the whole default object. Omitted keys fall back to the inline defaults ("ghost"for edit and cancel,"outline.dark"for save). - There is no keyboard shortcut for saving or cancelling while editing.
- The draft text resets to
initialTextwheneverinitialTextchanges or edit mode toggles.