Edit Layout

Arrange the form body, group inputs, and place the toolbar.

<Edit> renders a title, a header actions slot, and its children — the form. Because inputs compose directly as children, the layout is plain JSX: wrap inputs in your own containers, split them into columns, or group them into tabs and steps. The children mount once the record has loaded.

The form body

The simplest form is a stack of inputs followed by a toolbar:

import { Edit, TextInput, Toolbar } from "@chelone/react";
 
<Edit>
  <TextInput source="title" />
  <TextInput source="body" multiline />
  <Toolbar />
</Edit>;

Wrap inputs in ordinary layout elements to shape the form — Chelone imposes no form container:

import { Edit, NumberInput, TextInput, Toolbar } from "@chelone/react";
 
<Edit>
  <div className="grid grid-cols-2 gap-4">
    <TextInput source="title" />
    <NumberInput source="views" />
  </div>
  <TextInput source="body" multiline />
  <Toolbar />
</Edit>;

Tabs and steps

For long forms, <TabbedForm> splits inputs across tabs and <WizardForm> across sequential steps. Both compose as <Edit> children in place of a plain stack. Given no save prop, they drive the form <Edit> already built: the record hydration, the schema validation and the save pipeline stay in effect. Form options such as validate belong on <Edit>, not on the tabs or steps.

Neither renders a toolbar, so add one after them:

import { Edit, TabbedForm, TextInput, Toolbar } from "@chelone/react";
 
<Edit>
  <TabbedForm>
    <TabbedForm.Tab name="summary" label="Summary">
      <TextInput source="title" />
    </TabbedForm.Tab>
    <TabbedForm.Tab name="body" label="Body">
      <TextInput source="body" multiline />
    </TabbedForm.Tab>
  </TabbedForm>
  <Toolbar />
</Edit>;

Every tab stays mounted (inactive ones are hidden), so a single save submits all of them and a failing input on a hidden tab still blocks the save. A tab header shows a dot while its inputs are invalid. Tabs sync with the URL by default, which makes them deep-linkable; pass syncWithLocation={false} to keep the active tab in local state.

<WizardForm> works the same way, with <WizardForm.Step> children. It renders no navigation: drive it with useWizardFormContext. next() validates the current step and only advances when it passes:

import {
  Edit,
  SaveButton,
  TextInput,
  useWizardFormContext,
  WizardForm,
} from "@chelone/react";
 
function StepNav() {
  const { hasNext, hasPrev, next, prev } = useWizardFormContext();
  return (
    <div className="flex gap-2">
      <button type="button" disabled={!hasPrev} onClick={prev}>
        Back
      </button>
      {hasNext ? (
        <button type="button" onClick={() => next()}>
          Next
        </button>
      ) : (
        <SaveButton />
      )}
    </div>
  );
}
 
<Edit>
  <WizardForm>
    <WizardForm.Step name="summary">
      <TextInput source="title" />
    </WizardForm.Step>
    <WizardForm.Step name="body">
      <TextInput source="body" multiline />
    </WizardForm.Step>
    <StepNav />
  </WizardForm>
</Edit>;

The toolbar

With no children, <Toolbar> always renders Save and Delete. Pass children to replace them, or drop in a bare <SaveButton> if you want just the save action:

import { Edit, SaveButton, TextInput } from "@chelone/react";
 
<Edit>
  <TextInput source="title" />
  <SaveButton />
</Edit>;

Because the default toolbar includes Delete, <Create> uses a bare <SaveButton> too: there is nothing to delete yet.

Props

PropTypeDefaultDescription
actionsReactNode—Header actions (show/delete buttons); nothing by default.
childrenReactNode—
classNamestring—
componentElementType<any, keyof IntrinsicElements>—
idIdentifier—Override the record id (defaults to the `:id` route param).
onSuccess(record: TRecord) => void | Promise<void>—Runs after a successful save with the SERVER record, post-reconciliation.
resourcestring—Override the ambient resource (defaults to the `<Resource>` context).
sanitizeEmptyValuesboolean—ra parity: nullify/strip empty TOP-LEVEL values before save. Default false.
schemaStandardSchemaV1<Partial<Record<string, unknown>>, unknown>—Resource schema (zod or any Standard Schema) — wired as the default form validator by the submit pipeline (T2).
titleReactNode—Header title. A string/element renders as an `<h2>`; `false` omits it.
validateFormLevelValidate<Partial<Record<string, unknown>>>—Form-level validate fn (ra contract) — consumed by the submit pipeline (T2).

Tips

  • The component prop swaps the element wrapping the children (default a div) — pass a Card to frame the form.
  • title={false} drops the heading; actions={false} drops the header slot.