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 comprehensive components for creating, editing, and viewing individual records. These detail view components handle form management, validation, data persistence, and UI layout for single-record operations.
The <Show> component displays detailed information about a single record in read-only format.
import { Show } from 'react-admin';
interface ShowProps {
id?: Identifier;
resource?: string;
title?: string | React.ReactElement;
actions?: React.ReactElement | false;
aside?: React.ReactElement;
component?: React.ElementType;
className?: string;
sx?: any;
children: React.ReactNode;
emptyWhileLoading?: boolean;
queryOptions?: UseQueryOptions;
}
const Show: React.FC<ShowProps>;import { Show, SimpleShowLayout, TextField, DateField, EmailField } from 'react-admin';
const UserShow = () => (
<Show>
<SimpleShowLayout>
<TextField source="id" />
<TextField source="firstName" />
<TextField source="lastName" />
<EmailField source="email" />
<DateField source="createdAt" />
</SimpleShowLayout>
</Show>
);import { Show, SimpleShowLayout, TopToolbar, EditButton, DeleteButton } from 'react-admin';
const PostShow = () => {
const PostShowActions = () => (
<TopToolbar>
<EditButton />
<DeleteButton />
</TopToolbar>
);
return (
<Show actions={<PostShowActions />}>
<SimpleShowLayout>
<TextField source="title" />
<TextField source="content" />
<DateField source="publishedAt" showTime />
<BooleanField source="published" />
</SimpleShowLayout>
</Show>
);
};The <Edit> component provides form-based editing capabilities for existing records.
import { Edit } from 'react-admin';
interface EditProps {
id?: Identifier;
resource?: string;
title?: string | React.ReactElement;
actions?: React.ReactElement | false;
aside?: React.ReactElement;
component?: React.ElementType;
className?: string;
sx?: any;
children: React.ReactNode;
transform?: TransformData;
onSuccess?: OnSuccess;
onError?: OnError;
mutationMode?: MutationMode;
mutationOptions?: UseMutationOptions;
queryOptions?: UseQueryOptions;
redirect?: RedirectToFunction | string | false;
disableAuthentication?: boolean;
}
const Edit: React.FC<EditProps>;import { Edit, SimpleForm, TextInput, BooleanInput, SaveButton } from 'react-admin';
const PostEdit = () => (
<Edit>
<SimpleForm>
<TextInput source="title" required />
<TextInput source="content" multiline rows={4} />
<BooleanInput source="published" />
<SaveButton />
</SimpleForm>
</Edit>
);import { Edit, SimpleForm, TextInput, required, minLength } from 'react-admin';
const PostEdit = () => {
const transform = (data) => ({
...data,
slug: data.title?.toLowerCase().replace(/\s+/g, '-'),
updatedAt: new Date().toISOString()
});
return (
<Edit
transform={transform}
mutationMode="optimistic"
>
<SimpleForm>
<TextInput
source="title"
validate={[required(), minLength(5)]}
/>
<TextInput
source="content"
validate={required()}
multiline
rows={6}
/>
<BooleanInput source="published" />
</SimpleForm>
</Edit>
);
};The <Create> component handles creation of new records with form validation and submission.
import { Create } from 'react-admin';
interface CreateProps {
resource?: string;
title?: string | React.ReactElement;
actions?: React.ReactElement | false;
aside?: React.ReactElement;
component?: React.ElementType;
className?: string;
sx?: any;
children: React.ReactNode;
record?: Partial<RaRecord>;
transform?: TransformData;
onSuccess?: OnSuccess;
onError?: OnError;
mutationMode?: MutationMode;
mutationOptions?: UseMutationOptions;
redirect?: RedirectToFunction | string | false;
disableAuthentication?: boolean;
}
const Create: React.FC<CreateProps>;import { Create, SimpleForm, TextInput, BooleanInput, required } from 'react-admin';
const PostCreate = () => (
<Create>
<SimpleForm>
<TextInput source="title" validate={required()} />
<TextInput source="content" multiline rows={4} />
<BooleanInput source="published" defaultValue={false} />
</SimpleForm>
</Create>
);import { Create, SimpleForm, TextInput, SelectInput } from 'react-admin';
const PostCreate = () => {
const defaultValues = {
status: 'draft',
author: 'current-user-id',
createdAt: new Date().toISOString()
};
return (
<Create record={defaultValues}>
<SimpleForm>
<TextInput source="title" required />
<TextInput source="content" multiline rows={4} />
<SelectInput
source="status"
choices={[
{ id: 'draft', name: 'Draft' },
{ id: 'published', name: 'Published' }
]}
/>
</SimpleForm>
</Create>
);
};Basic vertical layout for show views.
import { SimpleShowLayout } from 'react-admin';
interface SimpleShowLayoutProps {
children: React.ReactNode;
className?: string;
sx?: any;
spacing?: number;
direction?: 'row' | 'column';
}
const SimpleShowLayout: React.FC<SimpleShowLayoutProps>;Tabbed layout for organizing show fields into sections.
import { TabbedShowLayout, Tab } from 'react-admin';
interface TabbedShowLayoutProps {
children: React.ReactNode;
className?: string;
sx?: any;
tabs?: React.ComponentType;
value?: number | string;
indicator?: React.ComponentType;
}
const TabbedShowLayout: React.FC<TabbedShowLayoutProps>;
interface TabProps {
label: string;
icon?: React.ReactElement;
value?: string | number;
count?: number;
children: React.ReactNode;
className?: string;
sx?: any;
}
const Tab: React.FC<TabProps>;import { Show, TabbedShowLayout, Tab, TextField, DateField, ReferenceManyField, Datagrid } from 'react-admin';
const UserShow = () => (
<Show>
<TabbedShowLayout>
<Tab label="General">
<TextField source="firstName" />
<TextField source="lastName" />
<EmailField source="email" />
<DateField source="createdAt" />
</Tab>
<Tab label="Profile">
<TextField source="bio" />
<TextField source="website" />
<TextField source="location" />
</Tab>
<Tab label="Posts" count={(record) => record.postCount}>
<ReferenceManyField reference="posts" target="authorId">
<Datagrid>
<TextField source="title" />
<DateField source="publishedAt" />
</Datagrid>
</ReferenceManyField>
</Tab>
</TabbedShowLayout>
</Show>
);Basic form layout for edit and create views.
import { SimpleForm } from 'react-admin';
interface SimpleFormProps {
children: React.ReactNode;
defaultValues?: any;
validate?: ValidateForm;
onSubmit?: (data: any) => void | Promise<void>;
toolbar?: React.ReactElement | false;
warnWhenUnsavedChanges?: boolean;
sanitizeEmptyValues?: boolean;
className?: string;
sx?: any;
}
const SimpleForm: React.FC<SimpleFormProps>;Tabbed form layout for organizing inputs into sections.
import { TabbedForm, FormTab } from 'react-admin';
interface TabbedFormProps {
children: React.ReactNode;
defaultValues?: any;
validate?: ValidateForm;
onSubmit?: (data: any) => void | Promise<void>;
toolbar?: React.ReactElement | false;
tabs?: React.ComponentType;
warnWhenUnsavedChanges?: boolean;
sanitizeEmptyValues?: boolean;
className?: string;
sx?: any;
}
const TabbedForm: React.FC<TabbedFormProps>;
interface FormTabProps {
label: string;
icon?: React.ReactElement;
value?: string | number;
children: React.ReactNode;
className?: string;
sx?: any;
}
const FormTab: React.FC<FormTabProps>;import { Edit, TabbedForm, FormTab, TextInput, NumberInput, BooleanInput } from 'react-admin';
const ProductEdit = () => (
<Edit>
<TabbedForm>
<FormTab label="General">
<TextInput source="name" required fullWidth />
<TextInput source="description" multiline rows={4} fullWidth />
<NumberInput source="price" required />
</FormTab>
<FormTab label="Inventory">
<NumberInput source="stock" required />
<NumberInput source="minStock" />
<BooleanInput source="trackInventory" />
</FormTab>
<FormTab label="SEO">
<TextInput source="metaTitle" fullWidth />
<TextInput source="metaDescription" multiline rows={2} fullWidth />
<TextInput source="slug" fullWidth />
</FormTab>
</TabbedForm>
</Edit>
);Button for saving form data.
import { SaveButton } from 'react-admin';
interface SaveButtonProps {
label?: string;
icon?: React.ReactElement;
disabled?: boolean;
variant?: 'text' | 'outlined' | 'contained';
color?: 'inherit' | 'primary' | 'secondary';
size?: 'small' | 'medium' | 'large';
alwaysEnable?: boolean;
transform?: TransformData;
mutationOptions?: UseMutationOptions;
type?: 'button' | 'submit';
className?: string;
sx?: any;
}
const SaveButton: React.FC<SaveButtonProps>;Button for deleting records.
import { DeleteButton } from 'react-admin';
interface DeleteButtonProps {
label?: string;
icon?: React.ReactElement;
disabled?: boolean;
className?: string;
sx?: any;
confirmTitle?: string;
confirmContent?: string | React.ReactElement;
redirect?: string | false;
mutationMode?: MutationMode;
mutationOptions?: UseMutationOptions;
}
const DeleteButton: React.FC<DeleteButtonProps>;Navigation button to edit view.
import { EditButton } from 'react-admin';
interface EditButtonProps {
label?: string;
icon?: React.ReactElement;
className?: string;
sx?: any;
record?: RaRecord;
resource?: string;
}
const EditButton: React.FC<EditButtonProps>;Navigation button to show view.
import { ShowButton } from 'react-admin';
const ShowButton: React.FC<EditButtonProps>;import { Toolbar, SaveButton, DeleteButton } from 'react-admin';
const PostEditToolbar = () => (
<Toolbar>
<SaveButton />
<SaveButton
label="Save and Continue"
transform={data => ({ ...data, continueEditing: true })}
variant="outlined"
/>
<DeleteButton />
</Toolbar>
);
const PostEdit = () => (
<Edit>
<SimpleForm toolbar={<PostEditToolbar />}>
<TextInput source="title" />
<TextInput source="content" multiline />
</SimpleForm>
</Edit>
);import { TopToolbar, Button, useRecordContext, useRedirect, useNotify } from 'react-admin';
const PostShowActions = () => {
const record = useRecordContext();
const redirect = useRedirect();
const notify = useNotify();
const handleClone = () => {
redirect('create', 'posts', undefined, undefined, {
record: { ...record, id: undefined, title: `Copy of ${record.title}` }
});
notify('Ready to create a copy');
};
return (
<TopToolbar>
<EditButton />
<Button label="Clone" onClick={handleClone} />
<DeleteButton />
</TopToolbar>
);
};import { Edit, SimpleForm, TextInput } from 'react-admin';
const validatePost = (values) => {
const errors: any = {};
if (!values.title) {
errors.title = 'Title is required';
} else if (values.title.length < 5) {
errors.title = 'Title must be at least 5 characters';
}
if (!values.content) {
errors.content = 'Content is required';
}
if (values.publishedAt && new Date(values.publishedAt) > new Date()) {
errors.publishedAt = 'Publication date cannot be in the future';
}
return errors;
};
const PostEdit = () => (
<Edit>
<SimpleForm validate={validatePost}>
<TextInput source="title" />
<TextInput source="content" multiline />
<DateTimeInput source="publishedAt" />
</SimpleForm>
</Edit>
);const asyncValidatePost = async (values) => {
const errors: any = {};
if (values.slug) {
const response = await fetch(`/api/check-slug?slug=${values.slug}`);
const result = await response.json();
if (!result.available) {
errors.slug = 'This slug is already taken';
}
}
return errors;
};import { Edit, SimpleForm, useRedirect, useNotify } from 'react-admin';
const PostEdit = () => {
const redirect = useRedirect();
const notify = useNotify();
const handleSuccess = (data) => {
notify('Post updated successfully');
if (data.published) {
redirect('list', 'posts');
} else {
redirect('show', 'posts', data.id);
}
};
return (
<Edit onSuccess={handleSuccess}>
<SimpleForm>
<TextInput source="title" />
<BooleanInput source="published" />
</SimpleForm>
</Edit>
);
};import { Edit, SimpleForm, TextInput } from 'react-admin';
const PostEdit = () => {
const transform = (data) => ({
...data,
slug: data.title?.toLowerCase()
.replace(/[^\w ]+/g, '')
.replace(/ +/g, '-'),
updatedAt: new Date().toISOString(),
wordCount: data.content?.split(' ').length || 0
});
return (
<Edit transform={transform}>
<SimpleForm>
<TextInput source="title" />
<TextInput source="content" multiline />
</SimpleForm>
</Edit>
);
};import { Edit, SimpleForm, useNotify } from 'react-admin';
const PostEdit = () => {
const notify = useNotify();
const handleError = (error) => {
if (error.status === 403) {
notify('You do not have permission to edit this post', { type: 'error' });
} else if (error.status === 409) {
notify('This post has been modified by another user', { type: 'error' });
} else {
notify('An error occurred while saving', { type: 'error' });
}
};
return (
<Edit onError={handleError}>
<SimpleForm>
<TextInput source="title" />
<TextInput source="content" multiline />
</SimpleForm>
</Edit>
);
};import { Edit, TabbedForm, FormTab, FormDataConsumer, BooleanInput, TextInput } from 'react-admin';
const ProductEdit = () => (
<Edit>
<TabbedForm>
<FormTab label="General">
<TextInput source="name" fullWidth />
<BooleanInput source="hasVariants" />
<FormDataConsumer>
{({ formData, ...rest }) =>
formData.hasVariants && (
<TextInput
source="variantOptions"
label="Variant Options (comma separated)"
fullWidth
{...rest}
/>
)
}
</FormDataConsumer>
</FormTab>
</TabbedForm>
</Edit>
);import { Show, TabbedShowLayout, Tab, ReferenceManyField, Datagrid, CreateButton } from 'react-admin';
const OrderShow = () => (
<Show>
<TabbedShowLayout>
<Tab label="Order Info">
<TextField source="orderNumber" />
<DateField source="orderDate" />
<TextField source="status" />
<NumberField source="total" options={{ style: 'currency', currency: 'USD' }} />
</Tab>
<Tab label="Items">
<ReferenceManyField
reference="orderItems"
target="orderId"
label="Order Items"
>
<Datagrid>
<TextField source="productName" />
<NumberField source="quantity" />
<NumberField source="price" options={{ style: 'currency', currency: 'USD' }} />
<NumberField source="total" options={{ style: 'currency', currency: 'USD' }} />
</Datagrid>
</ReferenceManyField>
</Tab>
</TabbedShowLayout>
</Show>
);import { Edit, SimpleForm, Card, CardContent, Typography } from 'react-admin';
const PostAside = () => (
<Card sx={{ width: 300, margin: 2 }}>
<CardContent>
<Typography variant="h6">Publication Tips</Typography>
<Typography variant="body2">
• Keep titles under 60 characters for SEO
• Use engaging opening paragraphs
• Include relevant tags
• Preview before publishing
</Typography>
</CardContent>
</Card>
);
const PostEdit = () => (
<Edit aside={<PostAside />}>
<SimpleForm>
<TextInput source="title" fullWidth />
<TextInput source="content" multiline rows={10} fullWidth />
<ReferenceArrayInput source="tagIds" reference="tags">
<AutocompleteInput optionText="name" />
</ReferenceArrayInput>
</SimpleForm>
</Edit>
);React Admin's detail view system provides comprehensive functionality for creating sophisticated single-record interfaces with flexible layouts, powerful form handling, and extensive customization options for complex data management scenarios.
Install with Tessl CLI
npx tessl i tessl/npm-react-admin