ACF UI
Components

Post Object

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

Featured Post

Select a post to feature on the homepage

Installation

CLI

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

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 { PostObject } from "@/components/ui/post-object"
<PostObject
  items={posts}
  selectedItems={selectedPosts}
  onChange={(items) => setSelectedPosts(items)}
  placeholder="Select posts..."
  allowNull
/>

Examples

Basic

A basic post object field with static data and search functionality.

Featured Post

Select a post to feature on the homepage

Multiple Selection

Configure for multiple post selection with filters and sortable display.

Related Content

Select up to 5 related posts to display in the sidebar

Choose posts that are related to the current content. You can select up to 5 posts.

Async Loading

Load posts dynamically with search and filtering. Perfect for large datasets.

Dynamic Content Loader

Search and filter from a large dataset with async loading

Start typing to search through our content database

Data Structure

PostObjectItem

The component expects items to follow this structure:

interface PostObjectItem {
  id: string | number
  title: string
  type?: string
  status?: "publish" | "draft" | "private"
  taxonomy?: string[]
  featured_image?: string
  excerpt?: string
  date?: string
  url?: string
}

Example Data

const posts: PostObjectItem[] = [
  {
    id: 1,
    title: "Getting Started with React",
    type: "post",
    status: "publish",
    taxonomy: ["React", "JavaScript"],
    excerpt: "Learn the basics of React development...",
    date: "2024-01-15",
    url: "/posts/getting-started-react"
  },
  // ... more items
]

Async Loading

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

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

<PostObject
  onLoad={fetchPosts}
  selectedItems={selected}
  onChange={setSelected}
  postTypes={["post", "page"]}
  postStatuses={["publish", "draft"]}
  showFilters={["search", "post_type"]}
/>

Filtering Options

Control which filters are displayed using the showFilters prop:

// Show all filters
<PostObject showFilters={true} />

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

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

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

Return Formats

Choose how selected items are returned:

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

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

Configuration Options

Single vs Multiple Selection

// Single selection (default)
<PostObject
  multiple={false}
  placeholder="Select a post..."
  onChange={(items) => {
    const post = items[0] // Single item
  }}
/>

// Multiple selection
<PostObject
  multiple={true}
  placeholder="Select posts..."
  sortable={true} // Enable drag-and-drop reordering
  onChange={(items) => {
    // Array of items
  }}
/>

Allow Null Option

<PostObject
  allowNull={true}  // Show "Clear selection" option
  required={false}  // Not required
  placeholder="Select a post (optional)..."
/>

API Reference

PostObject

PropTypeDefaultDescription
itemsPostObjectItem[][]Static items to display
selectedItemsPostObjectItem[][]Currently selected items
onChange(items: PostObjectItem[] | string[] | number[] | null) => void-Callback when selection changes
onLoad(filter: PostObjectFilter) => Promise<PostObjectItem[]>-Async data loading function
multiplebooleanfalseAllow multiple selections
allowNullbooleantrueAllow clearing selections
requiredbooleanfalseWhether field is required
postTypesstring[][]Available post types for filtering
postStatusesstring[][]Available post statuses for filtering
taxonomiesstring[][]Available taxonomies for filtering
showFiltersboolean | string[]falseWhich filters to show
showPreviewbooleanfalseShow preview links
showFeaturedImagebooleanfalseShow featured images
showExcerptbooleanfalseShow item excerpts
showDatebooleanfalseShow item dates
sortablebooleanfalseEnable drag-and-drop sorting
returnFormat"object" | "id""object"Format of returned data
placeholderstring"Select post..."Dropdown placeholder text
searchPlaceholderstring"Search posts..."Search input placeholder
noItemsTextstring"No posts found"Text when no items available
loadingTextstring"Loading..."Loading state text
instructionsstring-Help text above the field
errorstring-Error message to display
variant"default" | "error""default"Visual variant
size"sm" | "md" | "lg""md"Size variant

Event Handlers

PropTypeDescription
onSelect(item: PostObjectItem) => voidCalled when an item is selected
onRemove(item: PostObjectItem) => voidCalled when an item is removed

PostObjectItem Interface

interface PostObjectItem {
  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
}

PostObjectFilter Interface

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

Common Patterns

Content Management System

const [parentPage, setParentPage] = useState<PostObjectItem | null>(null)

<PostObject
  items={pages}
  selectedItems={parentPage ? [parentPage] : []}
  onChange={(items) => {
    const pageItems = items as PostObjectItem[]
    setParentPage(pageItems[0] || null)
  }}
  multiple={false}
  placeholder="Select parent page..."
  showFilters={["search"]}
  postTypes={["page"]}
  allowNull
/>
const [relatedPosts, setRelatedPosts] = useState<PostObjectItem[]>([])

<PostObject
  onLoad={fetchPosts}
  selectedItems={relatedPosts}
  onChange={(items) => setRelatedPosts(items as PostObjectItem[])}
  multiple={true}
  placeholder="Select related posts..."
  showFilters={["search", "post_type"]}
  postTypes={["post", "tutorial", "guide"]}
  sortable={true}
  returnFormat="id"
/>
const [featuredContent, setFeaturedContent] = useState<PostObjectItem | null>(null)

<PostObject
  items={content}
  selectedItems={featuredContent ? [featuredContent] : []}
  onChange={(items) => {
    const contentItems = items as PostObjectItem[]
    setFeaturedContent(contentItems[0] || null)
  }}
  placeholder="Select featured content..."
  showFilters={["search", "post_type", "post_status"]}
  postTypes={["post", "page", "product"]}
  postStatuses={["publish"]}
  showExcerpt={true}
  showFeaturedImage={true}
  allowNull
/>

Accessibility

The Post Object component follows accessibility best practices:

  • Keyboard Navigation - Full keyboard support with arrow keys and Enter
  • Screen Reader Support - Proper ARIA labels and live regions
  • Focus Management - Clear focus indicators and logical tab order
  • High Contrast - Supports high contrast mode
  • Combobox Pattern - Follows WAI-ARIA combobox guidelines

Comparison with Relationship Field

FeaturePost ObjectRelationship
InterfaceDropdown/SelectDual-panel
Best forSingle or few selectionsMultiple selections
Screen spaceCompactMore spacious
Allow Null✅ Yes❌ No
SearchIn dropdownSeparate input
FilteringOptional filtersAlways visible
Use caseQuick selectionRelationship management