Skip to content

<ReferenceFieldBase>

<ReferenceFieldBase> is useful for displaying many-to-one and one-to-one relationships, e.g. the details of a user when rendering a post authored by that user. <ReferenceFieldBase> is a headless component, handling only the logic. This allows to use any UI library for the render. For a version based on MUI see <ReferenceField>

For instance, let’s consider a model where a post has one author from the users resource, referenced by a user_id field.

┌──────────────┐ ┌────────────────┐
│ posts │ │ users │
│--------------│ │----------------│
│ id │ ┌───│ id │
│ user_id │╾──┘ │ name │
│ title │ │ date_of_birth │
│ published_at │ └────────────────┘
└──────────────┘

In that case, use <ReferenceFieldBase> to display the post’s author as follows:

import { ShowBase, ReferenceFieldBase } from 'ra-core';
import { TextField } from './TextField';
export const PostShow = () => (
<ShowBase>
<div>
<TextField source="title" />
<ReferenceFieldBase source="user_id" reference="users" >
<UserView />
</ReferenceFieldBase>
</div>
</ShowBase>
);
export const UserView = () => {
const context = useReferenceFieldContext();
if (context.isPending) {
return <p>Loading...</p>;
}
if (context.error) {
return <p className="error">{context.error.toString()}</p>;
}
return <RecordRepresentation />;
};

<ReferenceFieldBase> fetches the data, puts it in a RecordContext, and its up to its children to handle the rendering by accessing the ReferencingContext using the useReferenceFieldContext hook.

It uses dataProvider.getMany() instead of dataProvider.getOne() for performance reasons. When using several <ReferenceFieldBase> in the same page (e.g. in a <DataTable>), this allows to call the dataProvider once instead of once per row.

PropRequiredTypeDefaultDescription
sourceRequiredstring-Name of the property to display
referenceRequiredstring-The name of the resource for the referenced records, e.g. ‘posts’
childrenOptionalReactNode-React component to render the referenced record.
renderOptional(context) => ReactNode-Function that takes the referenceFieldContext and renders the referenced record.
emptyOptionalReactNode-What to render when the field has no value or when the reference is missing
offlineOptionalReactNode-What to render when there is no network connectivity when loading the record
queryOptionsOptionalUseQuery Options{}react-query client options
recordOptionalRaRecord-The current record
sortByOptionalstring | FunctionsourceName of the field to use for sorting when used in a Datagrid

You can pass any component of your own as child, to render the related records as you wish. You can access the list context using the useReferenceFieldContext hook.

import { ReferenceFieldBase } from 'ra-core';
export const UserView = () => {
const { error, isPending, referenceRecord } = useReferenceFieldContext();
if (isPending) {
return <p>Loading...</p>;
}
if (error) {
return <p className="error">{error.toString()}</p>;
}
return <>{referenceRecord.name}</>;
};
export const MyReferenceField = () => (
<ReferenceFieldBase source="user_id" reference="users">
<UserView />
</ReferenceFieldBase>
);

<ReferenceFieldBase> can display a custom message when the referenced record is missing, thanks to the empty prop.

<ReferenceFieldBase source="user_id" reference="users" empty="Missing user" >
...
</ReferenceFieldBase>

<ReferenceFieldBase> renders the empty element when:

  • the referenced record is missing (no record in the users table with the right user_id), or
  • the field is empty (no user_id in the record).

You can pass either a React element or a string to the empty prop:

<ReferenceFieldBase source="user_id" reference="users" empty={<span>Missing user</span>} >
...
</ReferenceFieldBase>
<ReferenceFieldBase source="user_id" reference="users" empty="Missing user" >
...
</ReferenceFieldBase>

When the user is offline, <ReferenceFieldBase> is smart enough to display the referenced record if it was previously fetched. However, if the referenced record has never been fetched before, <ReferenceFieldBase> displays an error message explaining that the app has lost network connectivity.

You can customize this error message by passing a React element or a string to the offline prop:

<ReferenceFieldBase source="user_id" reference="users" offline={<span>No network, could not fetch data</span>} >
...
</ReferenceFieldBase>
<ReferenceFieldBase source="user_id" reference="users" offline="No network, could not fetch data" >
...
</ReferenceFieldBase>

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

For instance, to pass a custom meta:

<ReferenceFieldBase
source="user_id"
reference="users"
queryOptions={{ meta: { foo: 'bar' } }}
render={({ referenceRecord }) => referenceRecord.name}
>
...
</ReferenceFieldBase>

The resource to fetch for the related record.

For instance, if the posts resource has a user_id field, set the reference to users to fetch the user related to each post.

<ReferenceFieldBase source="user_id" reference="users" >
...
</ReferenceFieldBase>

Alternatively, you can pass a render function prop instead of children. This function will receive the ReferenceFieldContext as argument.

export const MyReferenceField = () => (
<ReferenceFieldBase
source="user_id"
reference="users"
render={({ error, isPending, referenceRecord }) => {
if (isPending) {
return <p>Loading...</p>;
}
if (error) {
return (
<p className="error">
{error.message}
</p>
);
}
return <p>{referenceRecord.name}</p>;
}}
/>
);

The render function prop will take priority on children props if both are set.

By default, when used in a <Datagrid>, and when the user clicks on the column header of a <ReferenceFieldBase>, ra-core sorts the list by the field source. To specify another field name to sort by, set the sortBy prop.

<ReferenceFieldBase source="user_id" reference="users" sortBy="user.name">
...
</ReferenceFieldBase>

When used in a list, <ReferenceFieldBase> fetches the referenced record only once for the entire table.

For instance, with this code:

import { ListBase, ListIterator, ReferenceFieldBase } from 'ra-core';
export const PostList = () => (
<ListBase>
<ListIterator>
<ReferenceFieldBase source="user_id" reference="users">
<AuthorView />
</ReferenceFieldBase>
</ListIterator>
</ListBase>
);

Ra-core accumulates and deduplicates the ids of the referenced records to make one dataProvider.getMany() call for the entire list, instead of n dataProvider.getOne() calls. So for instance, if the API returns the following list of posts:

[
{
id: 123,
title: 'Totally agree',
user_id: 789,
},
{
id: 124,
title: 'You are right my friend',
user_id: 789
},
{
id: 125,
title: 'Not sure about this one',
user_id: 735
}
]

Then ra-core renders the <PostList> with a loader for the <ReferenceFieldBase>, fetches the API for the related users in one call (dataProvider.getMany('users', { ids: [789,735] }), and re-renders the list once the data arrives. This accelerates the rendering and minimizes network load.

When you know that a page will contain a <ReferenceFieldBase>, you can configure the main page query to prefetch the referenced records to avoid a flicker when the data arrives. To do so, pass a meta.prefetch parameter to the page query.

For example, the following code prefetches the authors referenced by the posts:

const PostList = () => (
<ListBase queryOptions={{ meta: { prefetch: ['author'] } }}>
<ListIterator
render={({ title, author_id }) => (
<div>
<h3>{title}</h3>
<ReferenceFieldBase source="author_id" reference="authors">
<AuthorView />
</ReferenceFieldBase>
</div>
)}
/>
</ListBase>
);

Note: For prefetching to function correctly, your data provider must support Prefetching Relationships. Refer to your data provider’s documentation to verify if this feature is supported.

Note: Prefetching is a frontend performance feature, designed to avoid flickers and repaints. It doesn’t always prevent <ReferenceFieldBase> to fetch the data. For instance, when coming to a show view from a list view, the main record is already in the cache, so the page renders immediately, and both the page controller and the <ReferenceFieldBase> controller fetch the data in parallel. The prefetched data from the page controller arrives after the first render of the <ReferenceFieldBase>, so the data provider fetches the related data anyway. But from a user perspective, the page displays immediately, including the <ReferenceFieldBase>. If you want to avoid the <ReferenceFieldBase> to fetch the data, you can use the React Query Client’s staleTime option.

If your authProvider implements the canAccess method, React-Admin will verify whether users have access to the Show and Edit views.

For instance, given the following ReferenceFieldBase:

<ReferenceFieldBase source="user_id" reference="users" />

React-Admin will call canAccess with the following parameters:

  • If the users resource has a Show view: { action: "show", resource: 'posts', record: Object }
  • If the users resource has an Edit view: { action: "edit", resource: 'posts', record: Object }

And the link property of the referenceField context will be set accordingly. It will be set to false if the access is denied.