Guides
Defaults, unsaved-changes warning, custom toolbar, and more.
Default Values
The form is prepopulated based on the current RecordContext. For new records, or for empty fields, you can define the default values using the defaultValues prop. It must be an object, or a function returning an object, specifying default values for the created record. For instance:
const postDefaultValue = () => ({
id: uuid(),
created_at: new Date(),
nb_views: 0,
});
export const PostCreate = () => (
<Create>
<SimpleForm defaultValues={postDefaultValue}>
<TextInput source="title" />
<TextInput source="body" />
<NumberInput source="nb_views" />
</SimpleForm>
</Create>
);You can also define default values at the input level using the <Input defaultValue> prop:
export const PostCreate = () => (
<Create>
<SimpleForm>
<TextInput source="title" />
<TextInput source="body" />
<NumberInput source="nb_views" defaultValue={0} />
</SimpleForm>
</Create>
);Shadmin will ignore the Input default values if the Form already defines a global defaultValues (form > input).
Warn On Unsaved Changes
Shadmin keeps track of the form state, so it can detect when the user leaves an Edit or Create page with unsaved changes. To avoid data loss, you can use this ability to ask the user to confirm before leaving a page with unsaved changes.
Warning about unsaved changes is an opt-in feature: you must set the warnWhenUnsavedChanges prop in the form component to enable it:
export const TagEdit = () => (
<Edit>
<SimpleForm warnWhenUnsavedChanges>
<TextInput source="id" />
<TextInput source="name" />
...
</SimpleForm>
</Edit>
);Toolbar
The default <FormToolbar> renders two buttons:
<div className="flex flex-row gap-2 justify-end">
<CancelButton />
<SaveButton />
</div>You can provide a custom toolbar to override the form buttons:
const FormToolbar = () => (
<div className="flex flex-row gap-2 justify-end">
<DeleteButton />
<SaveButton variant="outline" />
</div>
);
const PostEdit = () => (
<Edit>
<SimpleForm toolbar={<FormToolbar />}>...</SimpleForm>
</Edit>
);Using Fields As Children
The basic usage of <SimpleForm> is to pass Input components as children. For non-editable fields, you can pass readOnly inputs, or even a <RecordField> component.
import {
Edit,
SimpleForm,
TextInput,
RecordField,
TextField,
} from "@/components/admin";
const PostEdit = () => (
<Edit>
<SimpleForm>
<TextInput source="id" />
<RecordField source="title" />
<TextInput source="body" />
</SimpleForm>
</Edit>
);Subscribing To Form Changes
You can grab the current form values using react-hook-form's useWatch hook. This allows to link two inputs, e.g., a country and a city input:
import { Edit, SimpleForm, SelectInput } from "@/components/admin";
import { useWatch } from "react-hook-form";
const countries = ["USA", "UK", "France"];
const cities = {
USA: ["New York", "Los Angeles", "Chicago", "Houston", "Phoenix"],
UK: ["London", "Birmingham", "Glasgow", "Liverpool", "Bristol"],
France: ["Paris", "Marseille", "Lyon", "Toulouse", "Nice"],
};
const toChoices = (items) => items.map((item) => ({ id: item, name: item }));
const CityInput = () => {
const country = useWatch({ name: "country" });
return (
<SelectInput
choices={country ? toChoices(cities[country]) : []}
source="cities"
/>
);
};
const OrderEdit = () => (
<Edit>
<SimpleForm>
<SelectInput source="country" choices={toChoices(countries)} />
<CityInput />
</SimpleForm>
</Edit>
);Headless Version
To render a form without wrapping div and without toolbar, use ra-core's <Form> component directly. It accepts the same props as <SimpleForm>, except className and toolbar.
This lets you implement custom layouts and submit behaviors.
import { Form } from "ra-core";
import { Edit, SaveButton, TextInput } from "@/components/admin";
const PostEdit = () => (
<Edit>
<Form>
<div className="grid gap-4 md:grid-cols-2">
<TextInput source="title" />
<TextInput source="author" />
<TextInput source="body" />
</div>
<SaveButton />
</Form>
</Edit>
);Access Control
If you need to hide some columns based on a set of permissions, wrap these columns with <CanAccess>.
import { CanAccess } from "ra-core";
import { Edit, SimpleForm, TextInput } from "@/components/admin";
const PostEdit = () => (
<Edit>
<SimpleForm>
<TextInput source="id" />
<TextInput source="title" />
<CanAccess action="write" resource="posts.author">
<TextInput source="author" />
</CanAccess>
<TextInput source="body" />
</SimpleForm>
</Edit>
);