ACF UI
Components

Relationship

A dual-column component for selecting one or more posts, pages, or custom content items with search and filtering capabilities.

Related Posts

Select up to 3 related posts to display

Choose posts that are related to the current content

Getting Started with React

post

Advanced TypeScript Patterns

post

Building UIs with Tailwind CSS

postdraft

State Management Guide

guide

API Design Best Practices

guide

Component Architecture

tutorial
No items selected
0 selected / 3 max
0 selected / 3 max

Installation

CLI

npx shadcn@latest add "https://acfui.com/r/relationship"
pnpm dlx shadcn@latest add "https://acfui.com/r/relationship"
yarn dlx shadcn@latest add "https://acfui.com/r/relationship"
bun x shadcn@latest add "https://acfui.com/r/relationship"

Manual

Install the following dependencies:

npm install lucide-react class-variance-authority
pnpm add lucide-react class-variance-authority
yarn add lucide-react class-variance-authority
bun add lucide-react class-variance-authority

Copy and paste the following code into your project.

Update the import paths to match your project setup.

Usage

import { Relationship } from "@/components/ui/relationship"
<Relationship
  items={posts}
  selectedItems={selectedPosts}
  onChange={(items) => setSelectedPosts(items)}
  multiple={true}
  max={5}
/>

Examples

Basic

A basic relationship field with static data, search, and filtering capabilities.

Related Posts

Select up to 3 related posts to display

Choose posts that are related to the current content

Getting Started with React

post

Advanced TypeScript Patterns

post

Building UIs with Tailwind CSS

postdraft

State Management Guide

guide

API Design Best Practices

guide

Component Architecture

tutorial
No items selected
0 selected / 3 max
0 selected / 3 max

Async Loading

Load data asynchronously with search and filtering. Perfect for large datasets that need server-side filtering.

Async Content Loader

Search and filter from a large dataset with async loading

Start typing to search through our content database

No items found
No items selected
0 selected / 5 max

Single Selection

Configure for single item selection with validation and error handling.

Page Settings
Configure the parent page for this content

Select the parent page where this content should be nested

Documentation

Main documentation section containing all guides and tutorials

pageNavigation

Blog

Blog section with articles and updates

pageContent

Products

Product catalog and information

pageCommerce

About

Company information and team details

pageCompany

Contact

Contact information and support channels

pageSupport

Legal

Legal documents and privacy policy

pagedraftLegal
No parent page selected
0 selected / 1 max

Advanced Configuration

Comprehensive example showcasing all relationship features with dynamic settings.

Advanced Relationship Configuration
Comprehensive example showcasing all relationship features

Select content items for your collection. Use filters to narrow down results.

React Performance Optimization

Learn advanced techniques to optimize React applications for better performance and user experience...

postReactPerformanceJavaScript

TypeScript Best Practices

Comprehensive guide to TypeScript best practices and coding patterns for enterprise applications...

postTypeScriptBest PracticesDevelopment

CSS Grid Mastery

Master CSS Grid layout with practical examples and real-world use cases...

tutorialCSSGridLayout

Next.js 14 Features

Explore the latest features in Next.js 14 and how to use them in your projects...

guideNext.jsReactFramework

Database Optimization

Optimize database queries and improve application performance with these proven techniques...

guidedraftDatabasePerformanceSQL

Microservices Architecture

Build scalable microservices architecture with Docker and Kubernetes...

tutorialArchitectureMicroservicesBackend

GraphQL API Design

Design efficient GraphQL APIs with proper schema design and resolver patterns...

postGraphQLAPIBackend

Testing Strategies

Comprehensive testing strategies for modern web applications...

guideTestingQADevelopment

DevOps Fundamentals

Learn DevOps fundamentals including CI/CD pipelines and infrastructure as code...

tutorialprivateDevOpsCI/CDInfrastructure

Security Best Practices

Essential security best practices for web applications and APIs...

guideSecurityWeb SecurityBest Practices
No items selected
Minimum 2 items required
0 selected / 5 max

Data Structure

RelationshipItem

The component expects items to follow this structure:

interface RelationshipItem {
  id: string | number          // Unique identifier
  title: string               // Display title
  type?: string              // Content type (post, page, etc.)
  status?: "publish" | "draft" | "private"  // Publication status
  taxonomy?: string[]        // Categories, tags, etc.
  featured_image?: string    // Image URL
  excerpt?: string          // Short description
  date?: string            // Publication date
  url?: string            // Preview URL
}

Example Data

const posts: RelationshipItem[] = [
  {
    id: 1,
    title: "Introduction to React Hooks",
    type: "post",
    status: "publish",
    taxonomy: ["React", "JavaScript"],
    excerpt: "Learn the fundamentals of React Hooks...",
    date: "2024-01-15",
    featured_image: "https://example.com/image.jpg",
    url: "/posts/react-hooks"
  },
  // ... more items
]

Async Loading

For large datasets, use the onLoad prop to fetch data asynchronously:

const fetchPosts = async (filter: RelationshipFilter): Promise<RelationshipItem[]> => {
  const response = await fetch('/api/posts', {
    method: 'POST',
    body: JSON.stringify(filter)
  })
  return response.json()
}

<Relationship
  onLoad={fetchPosts}
  selectedItems={selected}
  onChange={setSelected}
  postTypes={["post", "page"]}
  taxonomies={["category", "tag"]}
/>

The onLoad function receives a filter object with:

interface RelationshipFilter {
  search?: string           // Search query
  post_type?: string[]     // Filter by post types
  post_status?: string[]   // Filter by post statuses  
  taxonomy?: string[]      // Filter by taxonomies
}

Filtering Options

Control which filters are displayed using the showFilters prop:

// Show all filters (default)
<Relationship showFilters={true} />

// Show only search
<Relationship showFilters={["search"]} />

// Show search and post type filters
<Relationship showFilters={["search", "post_type"]} />

// Hide all filters
<Relationship showFilters={false} />

Display Options

Customize the item display with various options:

<Relationship
  showExcerpt={true}          // Show item excerpts
  showDate={true}             // Show publication dates
  showFeaturedImage={true}    // Show featured images
  showPreview={true}          // Show preview links
  sortable={true}             // Show drag handles (UI ready)
/>

Return Formats

Choose how selected items are returned:

// Return full objects (default)
<Relationship
  returnFormat="object"
  onChange={(items) => {
    // items: RelationshipItem[]
    console.log(items)
  }}
/>

// Return only IDs
<Relationship
  returnFormat="id"
  onChange={(items) => {
    // items: (string | number)[]
    console.log(items) // [1, 2, 3]
  }}
/>

Validation

Add validation rules for selection:

<Relationship
  required={true}           // Field is required
  min={1}                  // Minimum selections
  max={5}                  // Maximum selections
  error="Please select at least one item"
  variant="error"          // Error styling
/>

API Reference

Relationship

PropTypeDefault
size?
"sm" | "md" | "lg" | null
-
variant?
"default" | "error" | null
-
onTransitionStartCapture?
TransitionEventHandler<HTMLDivElement>
-
onTransitionStart?
TransitionEventHandler<HTMLDivElement>
-
onTransitionRunCapture?
TransitionEventHandler<HTMLDivElement>
-
onTransitionRun?
TransitionEventHandler<HTMLDivElement>
-
onTransitionEndCapture?
TransitionEventHandler<HTMLDivElement>
-
onTransitionEnd?
TransitionEventHandler<HTMLDivElement>
-
onTransitionCancelCapture?
TransitionEventHandler<HTMLDivElement>
-
onTransitionCancel?
TransitionEventHandler<HTMLDivElement>
-
onBeforeToggle?
ToggleEventHandler<HTMLDivElement>
-
onToggle?
ToggleEventHandler<HTMLDivElement>
-
onAnimationIterationCapture?
AnimationEventHandler<HTMLDivElement>
-
onAnimationIteration?
AnimationEventHandler<HTMLDivElement>
-
onAnimationEndCapture?
AnimationEventHandler<HTMLDivElement>
-
onAnimationEnd?
AnimationEventHandler<HTMLDivElement>
-
onAnimationStartCapture?
AnimationEventHandler<HTMLDivElement>
-
onAnimationStart?
AnimationEventHandler<HTMLDivElement>
-
onWheelCapture?
WheelEventHandler<HTMLDivElement>
-
onWheel?
WheelEventHandler<HTMLDivElement>
-
onScrollEndCapture?
UIEventHandler<HTMLDivElement>
-
onScrollEnd?
UIEventHandler<HTMLDivElement>
-
onScrollCapture?
UIEventHandler<HTMLDivElement>
-
onScroll?
UIEventHandler<HTMLDivElement>
-
onLostPointerCaptureCapture?
PointerEventHandler<HTMLDivElement>
-
onLostPointerCapture?
PointerEventHandler<HTMLDivElement>
-
onGotPointerCaptureCapture?
PointerEventHandler<HTMLDivElement>
-
onGotPointerCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerOutCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerOut?
PointerEventHandler<HTMLDivElement>
-
onPointerOverCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerOver?
PointerEventHandler<HTMLDivElement>
-
onPointerLeave?
PointerEventHandler<HTMLDivElement>
-
onPointerEnter?
PointerEventHandler<HTMLDivElement>
-
onPointerCancelCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerCancel?
PointerEventHandler<HTMLDivElement>
-
onPointerUpCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerUp?
PointerEventHandler<HTMLDivElement>
-
onPointerMoveCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerMove?
PointerEventHandler<HTMLDivElement>
-
onPointerDownCapture?
PointerEventHandler<HTMLDivElement>
-
onPointerDown?
PointerEventHandler<HTMLDivElement>
-
onTouchStartCapture?
TouchEventHandler<HTMLDivElement>
-
onTouchStart?
TouchEventHandler<HTMLDivElement>
-
onTouchMoveCapture?
TouchEventHandler<HTMLDivElement>
-
onTouchMove?
TouchEventHandler<HTMLDivElement>
-
onTouchEndCapture?
TouchEventHandler<HTMLDivElement>
-
onTouchEnd?
TouchEventHandler<HTMLDivElement>
-
onTouchCancelCapture?
TouchEventHandler<HTMLDivElement>
-
onTouchCancel?
TouchEventHandler<HTMLDivElement>
-
onSelectCapture?
ReactEventHandler<HTMLDivElement>
-
onMouseUpCapture?
MouseEventHandler<HTMLDivElement>
-
onMouseUp?
MouseEventHandler<HTMLDivElement>
-
onMouseOverCapture?
MouseEventHandler<HTMLDivElement>
-
onMouseOver?
MouseEventHandler<HTMLDivElement>
-
onMouseOutCapture?
MouseEventHandler<HTMLDivElement>
-
onMouseOut?
MouseEventHandler<HTMLDivElement>
-
onMouseMoveCapture?
MouseEventHandler<HTMLDivElement>
-
onMouseMove?
MouseEventHandler<HTMLDivElement>
-
onMouseLeave?
MouseEventHandler<HTMLDivElement>
-
onMouseEnter?
MouseEventHandler<HTMLDivElement>
-
onMouseDownCapture?
MouseEventHandler<HTMLDivElement>
-
onMouseDown?
MouseEventHandler<HTMLDivElement>
-
onDropCapture?
DragEventHandler<HTMLDivElement>
-
onDrop?
DragEventHandler<HTMLDivElement>
-
onDragStartCapture?
DragEventHandler<HTMLDivElement>
-
onDragStart?
DragEventHandler<HTMLDivElement>
-
onDragOverCapture?
DragEventHandler<HTMLDivElement>
-
onDragOver?
DragEventHandler<HTMLDivElement>
-
onDragLeaveCapture?
DragEventHandler<HTMLDivElement>
-
onDragLeave?
DragEventHandler<HTMLDivElement>
-
onDragExitCapture?
DragEventHandler<HTMLDivElement>
-
onDragExit?
DragEventHandler<HTMLDivElement>
-
onDragEnterCapture?
DragEventHandler<HTMLDivElement>
-
onDragEnter?
DragEventHandler<HTMLDivElement>
-
onDragEndCapture?
DragEventHandler<HTMLDivElement>
-
onDragEnd?
DragEventHandler<HTMLDivElement>
-
onDragCapture?
DragEventHandler<HTMLDivElement>
-
onDrag?
DragEventHandler<HTMLDivElement>
-
onDoubleClickCapture?
MouseEventHandler<HTMLDivElement>
-
onDoubleClick?
MouseEventHandler<HTMLDivElement>
-
onContextMenuCapture?
MouseEventHandler<HTMLDivElement>
-
onContextMenu?
MouseEventHandler<HTMLDivElement>
-
onClickCapture?
MouseEventHandler<HTMLDivElement>
-
onClick?
MouseEventHandler<HTMLDivElement>
-
onAuxClickCapture?
MouseEventHandler<HTMLDivElement>
-
onAuxClick?
MouseEventHandler<HTMLDivElement>
-
onWaitingCapture?
ReactEventHandler<HTMLDivElement>
-
onWaiting?
ReactEventHandler<HTMLDivElement>
-
onVolumeChangeCapture?
ReactEventHandler<HTMLDivElement>
-
onVolumeChange?
ReactEventHandler<HTMLDivElement>
-
onTimeUpdateCapture?
ReactEventHandler<HTMLDivElement>
-
onTimeUpdate?
ReactEventHandler<HTMLDivElement>
-
onSuspendCapture?
ReactEventHandler<HTMLDivElement>
-
onSuspend?
ReactEventHandler<HTMLDivElement>
-
onStalledCapture?
ReactEventHandler<HTMLDivElement>
-
onStalled?
ReactEventHandler<HTMLDivElement>
-
onSeekingCapture?
ReactEventHandler<HTMLDivElement>
-
onSeeking?
ReactEventHandler<HTMLDivElement>
-
onSeekedCapture?
ReactEventHandler<HTMLDivElement>
-
onSeeked?
ReactEventHandler<HTMLDivElement>
-
onRateChangeCapture?
ReactEventHandler<HTMLDivElement>
-
onRateChange?
ReactEventHandler<HTMLDivElement>
-
onProgressCapture?
ReactEventHandler<HTMLDivElement>
-
onProgress?
ReactEventHandler<HTMLDivElement>
-
onPlayingCapture?
ReactEventHandler<HTMLDivElement>
-
onPlaying?
ReactEventHandler<HTMLDivElement>
-
onPlayCapture?
ReactEventHandler<HTMLDivElement>
-
onPlay?
ReactEventHandler<HTMLDivElement>
-
onPauseCapture?
ReactEventHandler<HTMLDivElement>
-
onPause?
ReactEventHandler<HTMLDivElement>
-
onLoadStartCapture?
ReactEventHandler<HTMLDivElement>
-
onLoadStart?
ReactEventHandler<HTMLDivElement>
-
onLoadedMetadataCapture?
ReactEventHandler<HTMLDivElement>
-
onLoadedMetadata?
ReactEventHandler<HTMLDivElement>
-
onLoadedDataCapture?
ReactEventHandler<HTMLDivElement>
-
onLoadedData?
ReactEventHandler<HTMLDivElement>
-
onEndedCapture?
ReactEventHandler<HTMLDivElement>
-
onEnded?
ReactEventHandler<HTMLDivElement>
-
onEncryptedCapture?
ReactEventHandler<HTMLDivElement>
-
onEncrypted?
ReactEventHandler<HTMLDivElement>
-
onEmptiedCapture?
ReactEventHandler<HTMLDivElement>
-
onEmptied?
ReactEventHandler<HTMLDivElement>
-
onDurationChangeCapture?
ReactEventHandler<HTMLDivElement>
-
onDurationChange?
ReactEventHandler<HTMLDivElement>
-
onCanPlayThroughCapture?
ReactEventHandler<HTMLDivElement>
-
onCanPlayThrough?
ReactEventHandler<HTMLDivElement>
-
onCanPlayCapture?
ReactEventHandler<HTMLDivElement>
-
onCanPlay?
ReactEventHandler<HTMLDivElement>
-
onAbortCapture?
ReactEventHandler<HTMLDivElement>
-
onAbort?
ReactEventHandler<HTMLDivElement>
-
onKeyUpCapture?
KeyboardEventHandler<HTMLDivElement>
-
onKeyUp?
KeyboardEventHandler<HTMLDivElement>
-
onKeyDownCapture?
KeyboardEventHandler<HTMLDivElement>
-
onKeyDown?
KeyboardEventHandler<HTMLDivElement>
-
onErrorCapture?
ReactEventHandler<HTMLDivElement>
-
onError?
ReactEventHandler<HTMLDivElement>
-
onLoadCapture?
ReactEventHandler<HTMLDivElement>
-
onInvalidCapture?
FormEventHandler<HTMLDivElement>
-
onInvalid?
FormEventHandler<HTMLDivElement>
-
onSubmitCapture?
FormEventHandler<HTMLDivElement>
-
onSubmit?
FormEventHandler<HTMLDivElement>
-
onResetCapture?
FormEventHandler<HTMLDivElement>
-
onReset?
FormEventHandler<HTMLDivElement>
-
onInputCapture?
FormEventHandler<HTMLDivElement>
-
onInput?
FormEventHandler<HTMLDivElement>
-
onBeforeInputCapture?
FormEventHandler<HTMLDivElement>
-
onBeforeInput?
InputEventHandler<HTMLDivElement>
-
onChangeCapture?
FormEventHandler<HTMLDivElement>
-
onBlurCapture?
FocusEventHandler<HTMLDivElement>
-
onBlur?
FocusEventHandler<HTMLDivElement>
-
onFocusCapture?
FocusEventHandler<HTMLDivElement>
-
onFocus?
FocusEventHandler<HTMLDivElement>
-
onCompositionUpdateCapture?
CompositionEventHandler<HTMLDivElement>
-
onCompositionUpdate?
CompositionEventHandler<HTMLDivElement>
-
onCompositionStartCapture?
CompositionEventHandler<HTMLDivElement>
-
onCompositionStart?
CompositionEventHandler<HTMLDivElement>
-
onCompositionEndCapture?
CompositionEventHandler<HTMLDivElement>
-
onCompositionEnd?
CompositionEventHandler<HTMLDivElement>
-
onPasteCapture?
ClipboardEventHandler<HTMLDivElement>
-
onPaste?
ClipboardEventHandler<HTMLDivElement>
-
onCutCapture?
ClipboardEventHandler<HTMLDivElement>
-
onCut?
ClipboardEventHandler<HTMLDivElement>
-
onCopyCapture?
ClipboardEventHandler<HTMLDivElement>
-
onCopy?
ClipboardEventHandler<HTMLDivElement>
-
dangerouslySetInnerHTML?
{ __html: string | TrustedHTML; }
-
children?
ReactNode
-
aria-valuetext?
string
-
aria-valuenow?
number
-
aria-valuemin?
number
-
aria-valuemax?
number
-
aria-sort?
"none" | "ascending" | "descending" | "other"
-
aria-setsize?
number
-
aria-selected?
Booleanish
-
aria-rowspan?
number
-
aria-rowindextext?
string
-
aria-rowindex?
number
-
aria-rowcount?
number
-
aria-roledescription?
string
-
aria-required?
Booleanish
-
aria-relevant?
"text" | "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text additions" | "text removals"
-
aria-readonly?
Booleanish
-
aria-pressed?
boolean | "true" | "false" | "mixed"
-
aria-posinset?
number
-
aria-placeholder?
string
-
aria-owns?
string
-
aria-orientation?
"horizontal" | "vertical"
-
aria-multiselectable?
Booleanish
-
aria-multiline?
Booleanish
-
aria-modal?
Booleanish
-
aria-live?
"off" | "assertive" | "polite"
-
aria-level?
number
-
aria-labelledby?
string
-
aria-label?
string
-
aria-keyshortcuts?
string
-
aria-invalid?
boolean | "true" | "false" | "grammar" | "spelling"
-
aria-hidden?
Booleanish
-
aria-haspopup?
boolean | "true" | "false" | "dialog" | "grid" | "listbox" | "menu" | "tree"
-
aria-flowto?
string
-
aria-expanded?
Booleanish
-
aria-errormessage?
string
-
aria-disabled?
Booleanish
-
aria-details?
string
-
aria-description?
string
-
aria-describedby?
string
-
aria-current?
boolean | "true" | "false" | "page" | "step" | "location" | "date" | "time"
-
aria-controls?
string
-
aria-colspan?
number
-
aria-colindextext?
string
-
aria-colindex?
number
-
aria-colcount?
number
-
aria-checked?
boolean | "true" | "false" | "mixed"
-
aria-busy?
Booleanish
-
aria-brailleroledescription?
string
-
aria-braillelabel?
string
-
aria-autocomplete?
"none" | "list" | "inline" | "both"
-
aria-atomic?
Booleanish
-
aria-activedescendant?
string
-
part?
string
-
exportparts?
string
-
is?
string
-
inputMode?
"none" | "search" | "text" | "tel" | "url" | "email" | "numeric" | "decimal"
-
inert?
boolean
-
popoverTarget?
string
-
popoverTargetAction?
"toggle" | "show" | "hide"
-
popover?
"" | "auto" | "manual"
-
unselectable?
"off" | "on"
-
security?
string
-
results?
number
-
itemRef?
string
-
itemID?
string
-
itemType?
string
-
itemScope?
boolean
-
itemProp?
string
-
color?
string
-
autoSave?
string
-
autoCorrect?
string
-
vocab?
string
-
typeof?
string
-
rev?
string
-
resource?
string
-
rel?
string
-
property?
string
-
prefix?
string
-
inlist?
any
-
datatype?
string
-
content?
string
-
about?
string
-
role?
AriaRole
-
radioGroup?
string
-
translate?
"yes" | "no"
-
title?
string
-
tabIndex?
number
-
style?
CSSProperties
-
spellCheck?
Booleanish
-
slot?
string
-
nonce?
string
-
lang?
string
-
id?
string
-
hidden?
boolean
-
enterKeyHint?
"enter" | "done" | "go" | "next" | "previous" | "search" | "send"
-
draggable?
Booleanish
-
dir?
string
-
contextMenu?
string
-
contentEditable?
Booleanish | "inherit" | "plaintext-only"
-
className?
string
-
autoFocus?
boolean
-
autoCapitalize?
"off" | "none" | "on" | "sentences" | "words" | "characters" | (string & {})
-
accessKey?
string
-
suppressHydrationWarning?
boolean
-
suppressContentEditableWarning?
boolean
-
defaultValue?
string | number | readonly string[]
-
defaultChecked?
boolean
-
error?
string
-
instructions?
string
-
loadingText?
string
-
noSelectedText?
string
-
noItemsText?
string
-
searchPlaceholder?
string
-
onRemove?
((item: RelationshipItem) => void)
-
onSelect?
((item: RelationshipItem) => void)
-
onChange?
((items: RelationshipItem[] | string[] | number[]) => void)
-
returnFormat?
"object" | "id"
-
sortable?
boolean
-
showDate?
boolean
-
showExcerpt?
boolean
-
showFeaturedImage?
boolean
-
showPreview?
boolean
-
showFilters?
boolean | string[]
-
taxonomies?
string[]
-
postStatuses?
string[]
-
postTypes?
string[]
-
loading?
boolean
-
onLoad?
((filter: RelationshipFilter) => Promise<RelationshipItem[]>)
-
required?
boolean
-
max?
number
-
min?
number
-
multiple?
boolean
-
selectedItems?
RelationshipItem[]
-
items?
RelationshipItem[]
-
aria-dropeffect?
"none" | "link" | "copy" | "execute" | "move" | "popup"
-
aria-grabbed?
Booleanish
-
onKeyPress?
KeyboardEventHandler<HTMLDivElement>
-
onKeyPressCapture?
KeyboardEventHandler<HTMLDivElement>
-

RelationshipItem Interface

interface RelationshipItem {
  id: string | number          // Unique identifier
  title: string               // Display title
  type?: string              // Post type (post, page, etc.)
  status?: "publish" | "draft" | "private"  // Publication status
  taxonomy?: string[]        // Categories, tags, etc.
  featured_image?: string    // Image URL
  excerpt?: string          // Short description
  date?: string            // Publication date
  url?: string            // Preview URL
}

RelationshipFilter Interface

interface RelationshipFilter {
  search?: string           // Search query
  post_type?: string[]     // Filter by post types
  post_status?: string[]   // Filter by post statuses
  taxonomy?: string[]      // Filter by taxonomies
}

Event Handlers

PropTypeDescription
onChange(items: RelationshipItem[] | string[] | number[]) => voidCalled when selection changes
onSelect(item: RelationshipItem) => voidCalled when an item is selected
onRemove(item: RelationshipItem) => voidCalled when an item is removed
onLoad(filter: RelationshipFilter) => Promise<RelationshipItem[]>Async data loading function

Configuration Props

PropTypeDefaultDescription
multiplebooleantrueAllow multiple selections
minnumber0Minimum required selections
maxnumber10Maximum allowed selections
requiredbooleanfalseWhether field is required
returnFormat"object" | "id""object"Format of returned data

Display Props

PropTypeDefaultDescription
showExcerptbooleanfalseShow item excerpts
showDatebooleanfalseShow item dates
showFeaturedImagebooleanfalseShow featured images
showPreviewbooleanfalseShow preview links
sortablebooleanfalseShow drag handles for sorting

Filter Props

PropTypeDefaultDescription
postTypesstring[][]Available post types for filtering
postStatusesstring[][]Available post statuses for filtering
taxonomiesstring[][]Available taxonomies for filtering
showFiltersboolean | string[]trueWhich filters to show

Text Props

PropTypeDefaultDescription
searchPlaceholderstring"Search..."Search input placeholder
noItemsTextstring"No items found"Text when no items available
noSelectedTextstring"No items selected"Text when nothing selected
loadingTextstring"Loading..."Loading state text
instructionsstring-Instructions text at top

Styling Props

PropTypeDefaultDescription
variant"default" | "error""default"Visual variant
size"sm" | "md" | "lg""md"Size variant
errorstring-Error message to display

Common Patterns

Content Management System

const [parentPage, setParentPage] = useState(null)

<Relationship
  items={pages}
  selectedItems={parentPage ? [parentPage] : []}
  onChange={(items) => setParentPage(items[0] || null)}
  multiple={false}
  max={1}
  required={true}
  showFilters={["search"]}
  error={errors.parent_page}
/>
const [relatedProducts, setRelatedProducts] = useState([])

<Relationship
  onLoad={fetchProducts}
  selectedItems={relatedProducts}
  onChange={setRelatedProducts}
  postTypes={["product"]}
  taxonomies={["product_cat", "product_tag"]}
  showFeaturedImage={true}
  showExcerpt={true}
  max={8}
  returnFormat="id"
/>

Blog Post Relations

const [relatedPosts, setRelatedPosts] = useState([])

<Relationship
  onLoad={async (filter) => {
    const response = await fetch('/api/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(filter)
    })
    return response.json()
  }}
  selectedItems={relatedPosts}
  onChange={setRelatedPosts}
  postTypes={["post", "page", "article"]}
  taxonomies={["category", "tag"]}
  showExcerpt={true}
  showFeaturedImage={true}
  max={5}
/>

Accessibility

The Relationship component follows accessibility best practices:

  • Keyboard Navigation - Full keyboard support for all interactions
  • Screen Reader Support - Proper ARIA labels and descriptions
  • Focus Management - Clear focus indicators and logical tab order
  • High Contrast - Supports high contrast mode
  • Reduced Motion - Respects prefers-reduced-motion settings

Keyboard Interactions

KeyDescription
TabMove focus between interface elements
EnterSelect/deselect items
EscapeClear focus from filters
Arrow KeysNavigate through available items