ObjectUIObjectUI
Fields

Lookup Field

Reference field for linking to other objects/records

The Lookup Field component provides a reference field for creating relationships between objects and records. It supports dynamic data loading from a DataSource with debounced search, loading/error/empty states, keyboard navigation, and optional quick-create entry.

Basic Usage

Basic Lookup

{
  "type": "lookup",
  "name": "assigned_to",
  "label": "Assigned To",
  "placeholder": "Select user...",
  "reference_to": "users",
  "options": [
    {
      "label": "John Doe",
      "value": "user_1"
    },
    {
      "label": "Jane Smith",
      "value": "user_2"
    },
    {
      "label": "Bob Johnson",
      "value": "user_3"
    }
  ]
}

Multiple References

Multi-Select Lookup

{
  "type": "lookup",
  "name": "team_members",
  "label": "Team Members",
  "placeholder": "Select team members...",
  "reference_to": "users",
  "multiple": true,
  "options": [
    {
      "label": "Alice Brown",
      "value": "user_4"
    },
    {
      "label": "Charlie Davis",
      "value": "user_5"
    },
    {
      "label": "Diana Wilson",
      "value": "user_6"
    }
  ]
}

Field Schema

interface LookupFieldSchema {
  type: 'lookup' | 'master_detail';
  name: string;                  // Field name/ID
  label?: string;                // Field label
  placeholder?: string;          // Placeholder text
  value?: string | string[];     // Default value(s)
  multiple?: boolean;            // Allow multiple selections
  required?: boolean;            // Is field required
  readonly?: boolean;            // Read-only mode
  disabled?: boolean;            // Disabled state
  className?: string;            // Additional CSS classes
  
  // Reference Configuration
  reference_to: string;          // Referenced object/table name
  reference_field?: string;      // Field to display (default: 'name')
  description_field?: string;    // Secondary field shown below label
  id_field?: string;             // ID field on records (default: '_id')
  options?: LookupOption[];      // Available options (if static)
  
  // Data Source (automatic via SchemaRendererContext, or explicit)
  // When a DataSource is available, the popup dynamically loads
  // records from the referenced object on open, with debounced search.
  dataSource?: DataSource;

  // Quick-create callback (shown when no results found)
  onCreateNew?: (searchQuery: string) => void;

  // === Record Picker Configuration (Enterprise) ===
  
  // Columns to show in the Record Picker dialog table.
  // Accepts field names or { field, label, width } objects.
  // When omitted, auto-infers from reference_field.
  lookup_columns?: Array<string | { field: string; label?: string; width?: string }>;

  // Custom page size for the Record Picker dialog (default: 10)
  lookup_page_size?: number;
}

interface LookupOption {
  label: string;                 // Display label
  value: string;                 // Record ID
  description?: string;          // Secondary text below label
  _id?: string;                  // Alternative ID field
  name?: string;                 // Alternative label field
}

Dynamic Data Source

When a DataSource is available (via SchemaRendererContext, explicit prop, or field config), the Lookup popup automatically fetches records from the referenced object:

// Automatic — DataSource from SchemaRendererContext
// (works out-of-the-box in ObjectForm, DrawerForm, etc.)
{
  type: 'lookup',
  name: 'customer',
  label: 'Customer',
  reference_to: 'customers',
  reference_field: 'name',        // Display field (default: 'name')
  description_field: 'industry',  // Optional secondary field
}

The popup will:

  1. Fetch records via dataSource.find(reference_to, { $top: 50 }) on open
  2. Send $search queries with 300ms debounce as the user types
  3. Show loading spinner, error state with retry, and empty state
  4. Display "Showing X of Y" when more records exist than the page size
  5. Show a "Show All Results" button (inside the popover) to open the full Record Picker dialog when total exceeds page size

Browse All Button

Every Lookup field with a dataSource always renders a "Browse All" button (table icon) next to the quick-select trigger. This button opens the full RecordPickerDialog directly, regardless of dataset size — ensuring enterprise features like multi-column tables, sort/filter bar, and cell renderers are always discoverable.

  • Always visible when dataSource is configured
  • Opens the Record Picker dialog without needing to open the popover first
  • Keyboard accessible and screen-reader friendly (aria-label="Browse all records")

Record Picker Dialog (Enterprise)

The full RecordPickerDialog can be opened in two ways:

  1. "Browse All" button (table icon) — always visible next to the quick-select trigger
  2. "Show All Results" link inside the popover — shown when total records exceed the page size
// Configure the Record Picker with lookup_columns
{
  type: 'lookup',
  name: 'order',
  label: 'Order',
  reference_to: 'orders',
  reference_field: 'order_number',
  description_field: 'customer_name',
  lookup_columns: [
    { field: 'order_number', label: 'Order #' },
    { field: 'customer_name', label: 'Customer' },
    { field: 'total_amount', label: 'Amount' },
    { field: 'status', label: 'Status' },
  ],
  lookup_page_size: 15,
}

The Record Picker dialog provides:

  • Multi-column table with configurable columns via lookup_columns
  • Search with debounced server-side querying
  • Column sorting via clickable headers (sends $orderby to DataSource)
  • Pagination with page-by-page navigation
  • Keyboard navigation — Arrow keys to move between rows, Enter/Space to select
  • Single/Multi-select with visual check indicators and confirmation flow
  • Responsive layout — Mobile-friendly width (95vw on small screens)
  • Loading, error, and empty states
  • Auto-inferred columns from reference_field when lookup_columns is not set

Lookup vs Master-Detail

  • Lookup: Standard reference field, can be deleted independently
  • Master-Detail: Parent-child relationship, deleting parent deletes children

Cell Renderer

In tables/grids, lookup values display the referenced record name:

import { LookupCellRenderer } from '@object-ui/fields';

// Single value: Display name/label
// Multiple values: Multiple chips/badges

Use Cases

  • Assignments: Assign tasks to users
  • Relationships: Link related records
  • Categories: Reference to category objects
  • Parent Records: Master-detail relationships
  • Team Members: Multi-user references

Features

  • Two-Level Interaction: Popover typeahead (Level 1) + full Record Picker dialog (Level 2)
  • Record Picker Dialog: Enterprise-grade table with multi-column, pagination, search, sorting
  • Inline Popover: Level 1 opens as anchored dropdown (non-modal) for fast typeahead
  • Column Sorting: Clickable column headers with $orderby server-side sort
  • Dynamic DataSource Loading: Automatically fetches records from referenced objects
  • Search: Debounced type-ahead search with $search parameter
  • Multi-Select: Support for multiple references with confirmation flow
  • Keyboard Navigation: Arrow keys to navigate rows, Enter to select in both levels
  • Responsive: Mobile-friendly width, adapts to screen size
  • Loading/Error/Empty States: Friendly feedback for all states
  • Secondary Field Display: Show description/subtitle per option
  • Quick-Create Entry: Optional "Create new" button when no results
  • Configurable Columns: lookup_columns for multi-column picker display
  • Base Filters: lookup_filters to restrict selectable records
  • Pagination: Page-by-page navigation in Record Picker dialog
  • Backward Compatible: Falls back to static options when no DataSource

On this page