<ReferenceManyField>

<ReferenceManyField> is useful for displaying a list of related records via a one-to-many relationship, when the foreign key is carried by the referenced resource.

This component fetches a list of referenced records by a reverse lookup of the current record.id in the target field of another resource (using the dataProvider.getManyReference() REST method), and puts them in a ListContext. Its children can then use the data from this context. The most common case is to use <SingleFieldList> or <Datagrid> as child.

Tip: If the relationship is materialized by an array of ids in the initial record, use the <ReferenceArrayField> component instead.

Tip: To edit the records of a one-to-many relationship, use the <ReferenceManyInput> component.

Usage

For instance, if an author has many books, and each book resource exposes an author_id field:

┌────────────────┐       ┌──────────────┐
│ authors        │       │ books        │
│----------------│       │--------------│
│ id             │───┐   │ id           │
│ first_name     │   └──╼│ author_id    │
│ last_name      │       │ title        │
│ date_of_birth  │       │ published_at │
└────────────────┘       └──────────────┘

<ReferenceManyField> can render the titles of all the books by a given author.

import { Show, SimpleShowLayout, ReferenceManyField, Datagrid, TextField, DateField } from 'react-admin';

const AuthorShow = () => (
    <Show>
        <SimpleShowLayout>
            <TextField source="first_name" />
            <TextField source="last_name" />
            <ReferenceManyField reference="books" target="author_id" label="Books">
              <Datagrid>
                <TextField source="title" />
                <DateField source="published_at" />
              </Datagrid>
            </ReferenceManyField>
        </SimpleShowLayout>
    </Show>
);

referenceManyField

<ReferenceManyField> accepts a reference attribute, which specifies the resource to fetch for the related record. It also accepts a source attribute which defines the field containing the value to look for in the target field of the referenced resource. By default, this is the id of the resource (authors.id in the previous example).

You can also use <ReferenceManyField> in a list, e.g. to display the authors of the comments related to each post in a list by matching post.id to comment.post_id:

import { List, Datagrid, ChipField, ReferenceManyField, SingleFieldList, TextField } from 'react-admin';

export const PostList = () => (
    <List>
        <Datagrid>
            <TextField source="id" />
            <TextField source="title" type="email" />
            <ReferenceManyField label="Comments by" reference="comments" target="post_id">
                <SingleFieldList>
                    <ChipField source="author.name" />
                </SingleFieldList>
            </ReferenceManyField>
            <EditButton />
        </Datagrid>
    </List>
);

ReferenceManyFieldSingleFieldList

This example leverages <SingleFieldList> to display an inline list using only one field for each of the referenced records.

Props

Prop Required Type Default Description
children Required Element - One or several elements that render a list of records based on a ListContext
debounce Optional number 500 debounce time in ms for the setFilters callbacks
filter Optional Object - Filters to use when fetching the related records, passed to getManyReference()
pagination Optional Element - Pagination element to display pagination controls. empty by default (no pagination)
perPage Optional number 25 Maximum number of referenced records to fetch
queryOptions Optional UseQuery Options {} react-query options for the getMany query
reference Required string - The name of the resource for the referenced records, e.g. ‘books’
sort Optional { field, order } { field: 'id', order: 'DESC' } Sort order to use when fetching the related records, passed to getManyReference()
source Optional string id Target field carrying the relationship on the source record (usually ‘id’)
storeKey Optional string - The key to use to store the records selection state
target Required string - Target field carrying the relationship on the referenced resource, e.g. ‘user_id’

<ReferenceManyField> also accepts the common field props, except emptyText (use the child empty prop instead).

children

<ReferenceManyField> renders its children inside a ListContext. This means you can use any component that uses a ListContext:

For instance, use a <Datagrid> to render the related records in a table:

import { Show, SimpleShowLayout, TextField, ReferenceManyField, Datagrid, DateField } from 'react-admin';

export const AuthorShow = () => (
  <Show>
    <SimpleShowLayout>
      <TextField source="first_name" />
      <TextField source="last_name" />
      <DateField label="Born" source="dob" />
      <ReferenceManyField label="Books" reference="books" target="author_id">
        <Datagrid>
          <TextField source="title" />
          <DateField source="published_at" />
        </Datagrid>
      </ReferenceManyField>
    </SimpleShowLayout>
  </Show>
);

Or <WithListContext> to render the related records in a custom way:

import { Show, SimpleShowLayout, TextField, ReferenceManyField, WithListContext, DateField } from 'react-admin';

export const AuthorShow = () => (
  <Show>
    <SimpleShowLayout>
      <TextField source="first_name" />
      <TextField source="last_name" />
      <DateField label="Born" source="dob" />
      <ReferenceManyField label="Books" reference="books" target="author_id">
        <WithListContext render={({ data }) => (
          <ul>
            {data.map(book => (
              <li key={book.id}>{book.title}</li>
            ))}
          </ul>
        )} />
      </ReferenceManyField>
    </SimpleShowLayout>
  </Show>
);

debounce

By default, <ReferenceManyField> does not refresh the data as soon as the user enters data in the filter form. Instead, it waits for half a second of user inactivity (via lodash.debounce) before calling the dataProvider on filter change. This is to prevent repeated (and useless) calls to the API.

You can customize the debounce duration in milliseconds - or disable it completely - by passing a debounce prop to the <ReferenceManyField> component:

// wait 1 seconds instead of 500 milliseconds before calling the dataProvider
const PostCommentsField = () => (
    <ReferenceManyField debounce={1000}>
        ...
    </ReferenceManyField>
);

filter

You can filter the query used to populate the possible values. Use the filter prop for that.

<ReferenceManyField
  reference="comments"
  target="post_id"
  filter={{ is_published: true }}
>
   ...
</ReferenceManyField>

label

By default, <SimpleShowLayout>, <Datagrid> and other layout components infer the label of a field based on its source. For a <ReferenceManyField>, the source defaults to id, so this may not be what you expect:

{/* default label is 'Id', or the translation of 'resources.authors.fields.id' if it exists */}
<ReferenceManyField reference="books" target="author_id">
  <Datagrid>
    <TextField source="title" />
    <DateField source="published_at" />
  </Datagrid>
</ReferenceManyField>

That’s why you often need to set an explicit label on a <ReferenceField>:

<ReferenceManyField label="Books" reference="books" target="author_id">
  <Datagrid>
    <TextField source="title" />
    <DateField source="published_at" />
  </Datagrid>
</ReferenceManyField>

React-admin uses the i18n system to translate the label, so you can use translation keys to have one label for each language supported by the interface:

<ReferenceManyField label="resources.authors.fields.books" reference="books" target="author_id">
  <Datagrid>
    <TextField source="title" />
    <DateField source="published_at" />
  </Datagrid>
</ReferenceManyField>

pagination

If you want to allow users to paginate the list, pass a <Pagination> element as the pagination prop:

import { Pagination } from 'react-admin';

<ReferenceManyField pagination={<Pagination />} reference="comments" target="post_id">
   ...
</ReferenceManyField>

perPage

By default, react-admin restricts the possible values to 25 and displays no pagination control. You can change the limit by setting the perPage prop:

<ReferenceManyField perPage={10} reference="comments" target="post_id">
   ...
</ReferenceManyField>

queryOptions

Use the queryOptions prop to pass options to the dataProvider.getMany() query that fetches the referenced record.

For instance, to pass a custom meta:

<ReferenceManyField queryOptions={{ meta: { foo: 'bar' } }} />

reference

The name of the resource to fetch for the related records.

For instance, if you want to display the books of a given author, the reference name should be books:

<ReferenceManyField label="Books" reference="books" target="author_id">
  <Datagrid>
    <TextField source="title" />
    <DateField source="published_at" />
  </Datagrid>
</ReferenceManyField>

sort

By default, it orders the possible values by id desc. You can change this order by setting the sort prop (an object with field and order properties).

<ReferenceManyField
  target="post_id"
  reference="comments"
  sort={{ field: 'created_at', order: 'DESC' }}
>
   ...
</ReferenceManyField>

source

By default, ReferenceManyField uses the id field as target for the reference. If the foreign key points to another field of your record, you can select it with the source prop.

<ReferenceManyField
  target="post_id"
  reference="comments"
  source="_id"
>
   ...
</ReferenceManyField>

storeKey

By default, react-admin stores the reference list selection state in localStorage so that users can come back to the list and find it in the same state as when they left it. React-admin uses the main resource, record id and reference resource as the identifier to store the selection state (under the key ${resource}.${record.id}.${reference}.selectedIds).

If you want to display multiple lists of the same reference and keep distinct selection states for each one, you must give each list a unique storeKey property.

In the example below, both lists use the same reference (‘books’), but their selection states are stored separately (under the store keys 'authors.1.books.selectedIds' and 'custom.selectedIds' respectively). This allows to use both components in the same page, each having its own state.

<Stack direction="row" spacing={2}>
    <ReferenceManyField
        reference="books"
        target="author_id"
        queryOptions={{
            meta: { foo: 'bar' },
        }}
    >
        <Datagrid>
            <TextField source="title" />
        </Datagrid>
    </ReferenceManyField>
    <ReferenceManyField
        reference="books"
        target="author_id"
        queryOptions={{
            meta: { foo: 'bar' },
        }}
        storeKey="custom"
    >
        <Datagrid>
            <TextField source="title" />
        </Datagrid>
    </ReferenceManyField>
</Stack>

target

Name of the field carrying the relationship on the referenced resource. For instance, if an author has many books, and each book resource exposes an author_id field, the target would be author_id.

<ReferenceManyField label="Books" reference="books" target="author_id">
  <Datagrid>
    <TextField source="title" />
    <DateField source="published_at" />
  </Datagrid>
</ReferenceManyField>

Rendering Only One Record

Although you are in a one-to-many relationship, you may want to render only one record. For instance, in a book with several reviews, you may want to display only the last. Or, for a product with many prices, you may want to display only the one in euros.

In these cases, use the <ReferenceOneField> component instead of <ReferenceManyField>.

<ReferenceOneField
    label="Latest review"
    reference="book_reviews"
    target="book_id"
    sort={{ field: "createdAt", order: "DESC" }}
>
    <RatingField />
    <TextField source="body" />
</ReferenceOneField>
<ReferenceOneField
    label="Price (€)"
    reference="product_prices"
    target="product_id"
    filter={{ currency: "EUR" }}
>
    <NumberField source="price" />
</ReferenceOneField>

To allow users to create or edit a record without leaving the current view, use the <CreateInDialogButton> or the <EditInDialogButton> component.

import { Edit, SimpleForm, TextInput, ReferenceManyField, WithRecord, Datagrid } from 'react-admin';
import { CreateInDialogButton, EditInDialogButton } from "@react-admin/ra-form-layout";

const EmployerEdit = () => (
  <Edit>
      <SimpleForm>
          <TextInput source="name" />
          <TextInput source="address" />
          <TextInput source="city" />
          <ReferenceManyField
              target="employer_id"
              reference="customers"
          >
              <WithRecord
                  render={record => (
                      <CreateInDialogButton
                          record={{ employer_id: record.id }}
                      >
                          <SimpleForm>
                              <TextInput source="first_name" />
                              <TextInput source="last_name" />
                          </SimpleForm>
                      </CreateInDialogButton>
                  )}
              />
              <Datagrid>
                  <TextField source="first_name" />
                  <TextField source="last_name" />
                  <WithRecord
                    render={record => (
                      <EditInDialogButton>
                          <SimpleForm
                            record={{ employer_id: record.id }}
                          >
                            <TextInput source="first_name" />
                            <TextInput source="last_name" />
                          </SimpleForm>
                        </EditInDialogButton>
                      )}
                  />
              </Datagrid>
          </ReferenceManyField>
      </SimpleForm>
  </Edit>
)