ReferenceField

ReferenceField

The <ReferenceField> component fetches and displays data from a related record. It's used to show relationships between resources in lists and show pages.

Usage

Display a related record's field:

import { DataTable, ReferenceField, TextField } from '@/components/admin';

const PostList = () => (
  <DataTable>
    <DataTable.Col source="title" />
    <DataTable.Col label="Author">
      <ReferenceField source="author_id" reference="users">
        <TextField source="name" />
      </ReferenceField>
    </DataTable.Col>
  </DataTable>
);

Installation

Terminal
npx better-admin add reference-field

Props

PropRequiredTypeDefaultDescription
sourceRequiredstring-Field containing the foreign key
referenceRequiredstring-Name of the related resource
childrenRequiredReactNode-Field(s) to display from related record
linkOptionalfalse | "show" | "edit""show"Navigation on click
emptyTextOptionalstring"-"Text when no relation found

With Multiple Fields

Display multiple fields from the related record:

<ReferenceField source="author_id" reference="users">
  <div className="flex flex-col">
    <TextField source="name" className="font-semibold" />
    <TextField source="email" className="text-sm text-muted-foreground" />
  </div>
</ReferenceField>

Control navigation behavior:

{/* Navigate to show page (default) */}
<ReferenceField source="author_id" reference="users">
  <TextField source="name" />
</ReferenceField>

{/* Navigate to edit page */}
<ReferenceField source="author_id" reference="users" link="edit">
  <TextField source="name" />
</ReferenceField>

{/* No navigation */}
<ReferenceField source="author_id" reference="users" link={false}>
  <TextField source="name" />
</ReferenceField>

With better-query

ReferenceField integrates with better-query for data fetching:

import { DataTable, ReferenceField, TextField } from '@/components/admin';
import { query } from '@/lib/query';

export function OrderList() {
  return (
    <DataTable>
      <DataTable.Col source="order_number" />
      <DataTable.Col label="Customer">
        <ReferenceField source="customer_id" reference="customers">
          <TextField source="name" />
        </ReferenceField>
      </DataTable.Col>
      <DataTable.Col label="Product">
        <ReferenceField source="product_id" reference="products">
          <TextField source="title" />
        </ReferenceField>
      </DataTable.Col>
    </DataTable>
  );
}

Empty State

Customize text when relation is missing:

<ReferenceField 
  source="author_id" 
  reference="users"
  emptyText="No author assigned"
>
  <TextField source="name" />
</ReferenceField>

Nested References

Display nested relationships:

<DataTable.Col label="Author's Country">
  <ReferenceField source="author_id" reference="users" link={false}>
    <ReferenceField source="country_id" reference="countries">
      <TextField source="name" />
    </ReferenceField>
  </ReferenceField>
</DataTable.Col>

With Custom Rendering

Use custom components to display related data:

import { ReferenceField } from '@/components/admin';
import { Avatar } from '@/components/ui/avatar';
import { useRecordContext } from '@/lib/hooks';

function UserAvatar() {
  const user = useRecordContext();
  return (
    <div className="flex items-center gap-2">
      <Avatar src={user?.avatar} alt={user?.name} />
      <span>{user?.name}</span>
    </div>
  );
}

// Usage
<ReferenceField source="user_id" reference="users">
  <UserAvatar />
</ReferenceField>

In Show Pages

Use ReferenceField in show layouts:

import { Show, SimpleShowLayout, ReferenceField, TextField } from '@/components/admin';

export const PostShow = () => (
  <Show>
    <SimpleShowLayout>
      <TextField source="title" />
      <ReferenceField source="author_id" reference="users" link="edit">
        <TextField source="name" />
      </ReferenceField>
      <ReferenceField source="category_id" reference="categories">
        <TextField source="name" />
      </ReferenceField>
    </SimpleShowLayout>
  </Show>
);

Complete Example

import {
  DataTable,
  ReferenceField,
  TextField,
  DateField,
  NumberField,
  EditButton,
} from '@/components/admin';

export const InvoiceList = () => (
  <DataTable>
    <DataTable.Col source="invoice_number" />
    
    <DataTable.Col label="Customer">
      <ReferenceField source="customer_id" reference="customers" link="show">
        <div className="flex flex-col">
          <TextField source="name" className="font-semibold" />
          <TextField source="email" className="text-xs text-muted-foreground" />
        </div>
      </ReferenceField>
    </DataTable.Col>
    
    <DataTable.Col label="Product">
      <ReferenceField source="product_id" reference="products">
        <TextField source="name" />
      </ReferenceField>
    </DataTable.Col>
    
    <DataTable.Col source="amount">
      <NumberField options={{ style: 'currency', currency: 'USD' }} />
    </DataTable.Col>
    
    <DataTable.Col source="date">
      <DateField />
    </DataTable.Col>
    
    <DataTable.Col label="Actions">
      <EditButton />
    </DataTable.Col>
  </DataTable>
);

Performance Optimization

ReferenceField automatically batches requests to avoid N+1 queries:

{/* These will be batched into a single request */}
<DataTable>
  <DataTable.Col label="Author">
    <ReferenceField source="author_id" reference="users">
      <TextField source="name" />
    </ReferenceField>
  </DataTable.Col>
  <DataTable.Col label="Category">
    <ReferenceField source="category_id" reference="categories">
      <TextField source="name" />
    </ReferenceField>
  </DataTable.Col>
</DataTable>

With Loading State

Handle loading state:

import { ReferenceField } from '@/components/admin';
import { Skeleton } from '@/components/ui/skeleton';

<ReferenceField 
  source="author_id" 
  reference="users"
  loading={<Skeleton className="h-4 w-24" />}
>
  <TextField source="name" />
</ReferenceField>

Next Steps

On this page