CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-admin

A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

detail-views.mddocs/

Detail Views

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.

Show Component

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>;

Basic Show Usage

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>
);

Show with Custom Actions

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>
  );
};

Edit Component

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>;

Basic Edit Usage

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>
);

Edit with Validation and Transform

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>
  );
};

Create Component

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>;

Basic Create Usage

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>
);

Create with Default Values

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>
  );
};

Layout Components

SimpleShowLayout

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>;

TabbedShowLayout

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>;

Tabbed Show Example

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>
);

Form Layout Components

SimpleForm

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>;

TabbedForm

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>;

Tabbed Form Example

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>
);

Action Components

SaveButton

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>;

DeleteButton

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>;

EditButton

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>;

ShowButton

Navigation button to show view.

import { ShowButton } from 'react-admin';

const ShowButton: React.FC<EditButtonProps>;

Custom Actions and Toolbars

Custom Toolbar

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>
);

Custom Actions

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>
  );
};

Validation in Detail Views

Form-Level Validation

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>
);

Async Validation

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;
};

Side Effects and Hooks

Redirect After Success

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>
  );
};

Transform Data Before Save

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>
  );
};

Error Handling

Custom Error Display

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>
  );
};

Advanced Examples

Conditional Fields in Forms

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>
);

Master-Detail Views

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>
);

Aside Panels

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

docs

admin-core.md

advanced.md

auth.md

data-management.md

detail-views.md

forms-inputs.md

i18n.md

index.md

layout-navigation.md

lists-data-display.md

ui-components.md

tile.json