A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
React Admin provides a comprehensive form system built on React Hook Form, offering rich input components, validation, and sophisticated form handling capabilities. The system supports complex scenarios including nested forms, arrays, relationships, and multi-step workflows.
The <Form> component is the foundation of form handling in React Admin, providing form context and validation.
import { Form } from 'react-admin';
interface FormProps {
defaultValues?: any;
record?: RaRecord;
validate?: ValidateForm;
resolver?: any;
onSubmit?: (data: any) => void | Promise<void>;
sanitizeEmptyValues?: boolean;
warnWhenUnsavedChanges?: boolean;
noValidate?: boolean;
children: React.ReactNode;
id?: string;
className?: string;
sx?: any;
}
const Form: React.FC<FormProps>;
type ValidateForm = (values: any) => any | Promise<any>;import { Form, TextInput, SaveButton } from 'react-admin';
const PostForm = ({ record, onSave }) => (
<Form
record={record}
onSubmit={onSave}
validate={(values) => {
const errors: any = {};
if (!values.title) errors.title = 'Title is required';
if (!values.content) errors.content = 'Content is required';
return errors;
}}
>
<TextInput source="title" label="Title" required />
<TextInput source="content" label="Content" multiline rows={4} />
<SaveButton />
</Form>
);Basic text input for single-line text.
import { TextInput } from 'react-admin';
interface TextInputProps {
source: string;
label?: string | false;
helperText?: string | false;
defaultValue?: any;
format?: (value: any) => any;
parse?: (value: any) => any;
validate?: Function | Function[];
required?: boolean;
disabled?: boolean;
readOnly?: boolean;
multiline?: boolean;
rows?: number;
maxRows?: number;
type?: 'text' | 'email' | 'url' | 'tel' | 'password';
placeholder?: string;
fullWidth?: boolean;
margin?: 'none' | 'dense' | 'normal';
variant?: 'standard' | 'outlined' | 'filled';
InputProps?: any;
inputProps?: any;
className?: string;
sx?: any;
}
const TextInput: React.FC<TextInputProps>;// Basic text input
<TextInput source="title" label="Post Title" />
// Multiline text input
<TextInput source="content" label="Content" multiline rows={4} />
// Email input with validation
<TextInput
source="email"
label="Email"
type="email"
validate={[required(), email()]}
/>
// Password input
<TextInput source="password" label="Password" type="password" />
// Custom format and parse
<TextInput
source="slug"
format={value => value?.toLowerCase().replace(/\s+/g, '-')}
parse={value => value?.toLowerCase().replace(/\s+/g, '-')}
/>Numeric input with number formatting and validation.
import { NumberInput } from 'react-admin';
interface NumberInputProps extends Omit<TextInputProps, 'type'> {
min?: number;
max?: number;
step?: number;
}
const NumberInput: React.FC<NumberInputProps>;<NumberInput
source="price"
label="Price"
min={0}
step={0.01}
validate={[required(), minValue(0)]}
/>Password input with show/hide toggle.
import { PasswordInput } from 'react-admin';
const PasswordInput: React.FC<TextInputProps>;Dropdown selection for choosing from predefined options.
import { SelectInput } from 'react-admin';
interface SelectInputProps {
source: string;
choices: ChoiceType[];
optionText?: string | Function;
optionValue?: string;
translateChoice?: boolean;
disableValue?: string | Function;
emptyText?: string;
emptyValue?: any;
label?: string | false;
validate?: Function | Function[];
defaultValue?: any;
fullWidth?: boolean;
margin?: 'none' | 'dense' | 'normal';
variant?: 'standard' | 'outlined' | 'filled';
className?: string;
sx?: any;
}
interface ChoiceType {
id: any;
name?: string;
[key: string]: any;
}
const SelectInput: React.FC<SelectInputProps>;const statusChoices = [
{ id: 'draft', name: 'Draft' },
{ id: 'published', name: 'Published' },
{ id: 'archived', name: 'Archived' }
];
<SelectInput
source="status"
choices={statusChoices}
label="Status"
defaultValue="draft"
/>Autocomplete input with search and filtering.
import { AutocompleteInput } from 'react-admin';
interface AutocompleteInputProps {
source: string;
choices: ChoiceType[];
optionText?: string | Function;
optionValue?: string;
matchSuggestion?: Function;
shouldRenderSuggestions?: Function;
inputText?: Function;
setFilter?: Function;
filterToQuery?: Function;
freeSolo?: boolean;
multiple?: boolean;
clearOnBlur?: boolean;
onCreate?: Function;
createLabel?: string;
createItemLabel?: string;
noOptionsText?: string;
label?: string | false;
validate?: Function | Function[];
debounce?: number;
className?: string;
sx?: any;
}
const AutocompleteInput: React.FC<AutocompleteInputProps>;const tagChoices = [
{ id: 1, name: 'Technology' },
{ id: 2, name: 'React' },
{ id: 3, name: 'JavaScript' }
];
<AutocompleteInput
source="tags"
choices={tagChoices}
multiple
create
label="Tags"
onCreate={(filter) => {
const newTag = { id: Date.now(), name: filter };
// Add to choices or save to backend
return newTag;
}}
/>Radio button group for single selection.
import { RadioButtonGroupInput } from 'react-admin';
interface RadioButtonGroupInputProps {
source: string;
choices: ChoiceType[];
optionText?: string | Function;
optionValue?: string;
translateChoice?: boolean;
label?: string | false;
validate?: Function | Function[];
row?: boolean;
className?: string;
sx?: any;
}
const RadioButtonGroupInput: React.FC<RadioButtonGroupInputProps>;Checkbox group for multiple selection.
import { CheckboxGroupInput } from 'react-admin';
interface CheckboxGroupInputProps {
source: string;
choices: ChoiceType[];
optionText?: string | Function;
optionValue?: string;
translateChoice?: boolean;
label?: string | false;
validate?: Function | Function[];
row?: boolean;
className?: string;
sx?: any;
}
const CheckboxGroupInput: React.FC<CheckboxGroupInputProps>;Checkbox for boolean values.
import { BooleanInput } from 'react-admin';
interface BooleanInputProps {
source: string;
label?: string | false;
helperText?: string | false;
defaultValue?: boolean;
format?: (value: any) => boolean;
parse?: (value: boolean) => any;
validate?: Function | Function[];
disabled?: boolean;
readOnly?: boolean;
color?: 'primary' | 'secondary' | 'default';
size?: 'small' | 'medium';
className?: string;
sx?: any;
}
const BooleanInput: React.FC<BooleanInputProps>;Three-state boolean input (true/false/null).
import { NullableBooleanInput } from 'react-admin';
const NullableBooleanInput: React.FC<SelectInputProps>;<NullableBooleanInput
source="featured"
label="Featured"
nullLabel="Not Set"
falseLabel="No"
trueLabel="Yes"
/>Date picker input.
import { DateInput } from 'react-admin';
interface DateInputProps {
source: string;
label?: string | false;
validate?: Function | Function[];
defaultValue?: Date | string;
format?: string;
parse?: (value: any) => any;
disabled?: boolean;
readOnly?: boolean;
fullWidth?: boolean;
margin?: 'none' | 'dense' | 'normal';
variant?: 'standard' | 'outlined' | 'filled';
className?: string;
sx?: any;
}
const DateInput: React.FC<DateInputProps>;Combined date and time picker.
import { DateTimeInput } from 'react-admin';
const DateTimeInput: React.FC<DateInputProps>;Time picker input.
import { TimeInput } from 'react-admin';
const TimeInput: React.FC<DateInputProps>;// Date input
<DateInput source="publishedAt" label="Published Date" />
// DateTime input with validation
<DateTimeInput
source="eventDate"
label="Event Date & Time"
validate={[required(), minValue(new Date())]}
/>
// Time input
<TimeInput source="reminderTime" label="Reminder Time" />File upload input with preview capabilities.
import { FileInput } from 'react-admin';
interface FileInputProps {
source: string;
accept?: string;
multiple?: boolean;
maxSize?: number;
minSize?: number;
placeholder?: React.ReactNode;
children?: React.ReactNode;
options?: any;
label?: string | false;
validate?: Function | Function[];
className?: string;
sx?: any;
}
const FileInput: React.FC<FileInputProps>;Specialized file input for images with preview.
import { ImageInput } from 'react-admin';
const ImageInput: React.FC<FileInputProps>;// Basic file input
<FileInput source="document" label="Upload Document">
<FileField source="src" title="title" />
</FileInput>
// Image input with preview
<ImageInput source="image" label="Product Image" accept="image/*">
<ImageField source="src" title="title" />
</ImageInput>
// Multiple file upload
<FileInput
source="attachments"
label="Attachments"
multiple
accept="application/pdf,image/*"
>
<FileField source="src" title="title" />
</FileInput>Input for selecting related records.
import { ReferenceInput } from 'react-admin';
interface ReferenceInputProps {
source: string;
reference: string;
children: React.ReactElement;
sort?: { field: string; order: 'ASC' | 'DESC' };
filter?: any;
perPage?: number;
enableGetChoices?: Function;
label?: string | false;
validate?: Function | Function[];
className?: string;
sx?: any;
}
const ReferenceInput: React.FC<ReferenceInputProps>;<ReferenceInput source="categoryId" reference="categories">
<SelectInput optionText="name" />
</ReferenceInput>
// With autocomplete
<ReferenceInput source="authorId" reference="users" sort={{ field: 'name', order: 'ASC' }}>
<AutocompleteInput optionText="name" />
</ReferenceInput>Input for selecting multiple related records.
import { ReferenceArrayInput } from 'react-admin';
interface ReferenceArrayInputProps {
source: string;
reference: string;
children: React.ReactElement;
sort?: { field: string; order: 'ASC' | 'DESC' };
filter?: any;
perPage?: number;
label?: string | false;
validate?: Function | Function[];
className?: string;
sx?: any;
}
const ReferenceArrayInput: React.FC<ReferenceArrayInputProps>;<ReferenceArrayInput source="tagIds" reference="tags">
<AutocompleteInput optionText="name" />
</ReferenceArrayInput>Input for editing arrays of objects.
import { ArrayInput } from 'react-admin';
interface ArrayInputProps {
source: string;
label?: string | false;
validate?: Function | Function[];
children: React.ReactElement;
className?: string;
sx?: any;
}
const ArrayInput: React.FC<ArrayInputProps>;import { ArrayInput, SimpleFormIterator, TextInput, NumberInput } from 'react-admin';
<ArrayInput source="items" label="Order Items">
<SimpleFormIterator>
<TextInput source="name" label="Item Name" />
<NumberInput source="quantity" label="Quantity" />
<NumberInput source="price" label="Price" />
</SimpleFormIterator>
</ArrayInput>Access and control individual input state.
import { useInput } from 'react-admin';
interface UseInputOptions {
defaultValue?: any;
format?: (value: any) => any;
parse?: (value: any) => any;
validate?: Function | Function[];
onBlur?: Function;
onChange?: Function;
source: string;
}
interface UseInputResult {
field: {
name: string;
value: any;
onChange: (event: any) => void;
onBlur: (event: any) => void;
};
fieldState: {
invalid: boolean;
isTouched: boolean;
isDirty: boolean;
error?: any;
};
formState: {
isSubmitted: boolean;
isSubmitting: boolean;
isValid: boolean;
errors: any;
};
}
const useInput: (options: UseInputOptions) => UseInputResult;import { useInput } from 'react-admin';
const CustomInput = ({ source, label, ...props }) => {
const {
field,
fieldState: { error, invalid, isTouched },
formState: { isSubmitted }
} = useInput({ source, ...props });
return (
<div>
<label>{label}</label>
<input {...field} />
{((isTouched && invalid) || (isSubmitted && invalid)) && (
<span style={{ color: 'red' }}>{error?.message}</span>
)}
</div>
);
};Access form state and methods.
import { useForm } from 'react-admin';
const useForm: () => {
getValues: (names?: string | string[]) => any;
setValue: (name: string, value: any, options?: any) => void;
reset: (values?: any) => void;
watch: (name?: string | string[], defaultValue?: any) => any;
formState: FormState;
control: Control;
register: Function;
handleSubmit: Function;
clearErrors: Function;
setError: Function;
trigger: Function;
};React Admin provides comprehensive validation capabilities.
import {
required,
minLength,
maxLength,
minValue,
maxValue,
number,
regex,
email,
choices
} from 'react-admin';
// Validation function types
type ValidatorFunction = (value: any, allValues?: any, props?: any) => string | undefined;
const required: (message?: string) => ValidatorFunction;
const minLength: (min: number, message?: string) => ValidatorFunction;
const maxLength: (max: number, message?: string) => ValidatorFunction;
const minValue: (min: number, message?: string) => ValidatorFunction;
const maxValue: (max: number, message?: string) => ValidatorFunction;
const number: (message?: string) => ValidatorFunction;
const regex: (pattern: RegExp, message?: string) => ValidatorFunction;
const email: (message?: string) => ValidatorFunction;
const choices: (list: any[], message?: string) => ValidatorFunction;import { required, minLength, maxLength, email, minValue } from 'react-admin';
// Single validator
<TextInput source="title" validate={required()} />
// Multiple validators
<TextInput
source="description"
validate={[required(), minLength(10), maxLength(500)]}
/>
// Email validation
<TextInput source="email" validate={[required(), email()]} />
// Number validation
<NumberInput
source="price"
validate={[required(), minValue(0)]}
/>
// Custom validation
<TextInput
source="username"
validate={(value) => {
if (!value) return 'Username is required';
if (value.length < 3) return 'Username must be at least 3 characters';
if (!/^[a-zA-Z0-9_]+$/.test(value)) return 'Username can only contain letters, numbers, and underscores';
return undefined;
}}
/>const asyncValidate = async (value) => {
if (!value) return 'Required';
const response = await fetch(`/api/check-username?username=${value}`);
const result = await response.json();
if (!result.available) {
return 'Username is already taken';
}
return undefined;
};
<TextInput source="username" validate={asyncValidate} />Access and react to form data changes.
import { FormDataConsumer } from 'react-admin';
interface FormDataConsumerProps {
children: (formDataConsumerProps: FormDataConsumerRenderParams) => React.ReactNode;
}
interface FormDataConsumerRenderParams {
formData: any;
scopedFormData?: any;
getSource?: (source: string) => string;
}
const FormDataConsumer: React.FC<FormDataConsumerProps>;import { FormDataConsumer, SelectInput, TextInput } from 'react-admin';
<FormDataConsumer>
{({ formData, ...rest }) => (
<>
<SelectInput source="type" choices={typeChoices} {...rest} />
{formData.type === 'book' && (
<TextInput source="isbn" label="ISBN" {...rest} />
)}
{formData.type === 'movie' && (
<TextInput source="director" label="Director" {...rest} />
)}
</>
)}
</FormDataConsumer>import { FormDataConsumer, BooleanInput, TextInput } from 'react-admin';
const ConditionalFieldsExample = () => (
<>
<BooleanInput source="hasDiscount" label="Has Discount" />
<FormDataConsumer>
{({ formData }) =>
formData.hasDiscount && (
<NumberInput
source="discountPercentage"
label="Discount %"
min={0}
max={100}
/>
)
}
</FormDataConsumer>
</>
);import {
Form,
TextInput,
NumberInput,
TabbedForm,
FormTab,
Toolbar,
SaveButton,
DeleteButton
} from 'react-admin';
const ProductForm = () => (
<TabbedForm>
<FormTab label="General">
<TextInput source="name" label="Product Name" />
<NumberInput source="price" label="Price" />
<TextInput source="description" multiline rows={4} />
</FormTab>
<FormTab label="Inventory">
<NumberInput source="stock" label="Stock Quantity" />
<NumberInput source="minStock" label="Minimum Stock" />
<BooleanInput source="trackInventory" label="Track Inventory" />
</FormTab>
<FormTab label="SEO">
<TextInput source="metaTitle" label="Meta Title" />
<TextInput source="metaDescription" label="Meta Description" multiline />
<TextInput source="slug" label="URL Slug" />
</FormTab>
<Toolbar>
<SaveButton />
<DeleteButton />
</Toolbar>
</TabbedForm>
);React Admin's form system provides powerful, flexible form handling with rich validation, sophisticated input components, and excellent developer experience for building complex data entry interfaces.
Install with Tessl CLI
npx tessl i tessl/npm-react-admin