PHPackages                             arraypress/wp-register-post-fields - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. arraypress/wp-register-post-fields

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

arraypress/wp-register-post-fields
==================================

Lightweight library for registering custom metaboxes with fields on WordPress post edit screens, featuring conditional field visibility, repeaters, and REST API integration.

24PHP

Since May 7Pushed 1mo agoCompare

[ Source](https://github.com/arraypress/wp-register-post-fields)[ Packagist](https://packagist.org/packages/arraypress/wp-register-post-fields)[ RSS](/packages/arraypress-wp-register-post-fields/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

WordPress Register Post Fields
==============================

[](#wordpress-register-post-fields)

A lightweight library for registering custom metaboxes with fields on WordPress post edit screens. This library provides a clean, simple API for adding common field types to any post type without complex configuration.

Features
--------

[](#features)

- **Simple API**: Register custom metaboxes with minimal code
- **30+ Field Types**: Comprehensive field type support for any use case
- **Conditional Logic**: Show/hide fields based on other field values with `show_when`
- **Repeater Fields**: Dynamic repeatable field groups with drag-and-drop reordering
- **Group Fields**: Static groups of related fields
- **AJAX-Powered Selects**: Searchable selects for posts, terms, users, and custom data
- **Automatic Saving**: Fields are automatically saved to post meta
- **Smart Sanitization**: Each field type has appropriate default sanitization
- **REST API Integration**: Fields are automatically registered with `register_meta()` and exposed via REST API
- **Dynamic Options**: Select fields support callable options for dynamic data
- **Multiple Post Types**: Register the same metabox across multiple post types
- **Permission Control**: Control field visibility based on user capabilities
- **Meta Key Prefixing**: Optional automatic prefixing of meta keys
- **Media Library Integration**: Native WordPress media picker for image, file, and gallery fields
- **Lightweight**: External CSS/JS assets, leverages WordPress built-in functionality

Requirements
------------

[](#requirements)

- PHP 7.4 or higher
- WordPress 5.0 or higher

Installation
------------

[](#installation)

Install via Composer:

```
composer require arraypress/wp-register-post-fields
```

Basic Usage
-----------

[](#basic-usage)

### Simple Metabox with Text Fields

[](#simple-metabox-with-text-fields)

```
register_post_fields( 'product_info', [
    'title'      => __( 'Product Information', 'textdomain' ),
    'post_types' => 'product',
    'fields'     => [
        'sku' => [
            'label'       => __( 'SKU', 'textdomain' ),
            'type'        => 'text',
            'placeholder' => 'PRD-0000',
        ],
        'price' => [
            'label' => __( 'Price', 'textdomain' ),
            'type'  => 'number',
            'min'   => 0,
            'step'  => 0.01,
        ],
    ],
] );
```

---

Field Types
-----------

[](#field-types)

### Basic Text Fields

[](#basic-text-fields)

#### Text

[](#text)

Standard single-line text input.

```
'field_name' => [
    'label'       => __( 'Field Label', 'textdomain' ),
    'type'        => 'text',
    'placeholder' => __( 'Placeholder text...', 'textdomain' ),
    'default'     => '',
]
```

#### URL

[](#url)

URL input with HTML5 validation.

```
'website' => [
    'label'       => __( 'Website', 'textdomain' ),
    'type'        => 'url',
    'placeholder' => 'https://example.com',
]
```

#### Email

[](#email)

Email input with HTML5 validation.

```
'contact_email' => [
    'label'       => __( 'Contact Email', 'textdomain' ),
    'type'        => 'email',
    'placeholder' => 'name@example.com',
]
```

#### Telephone

[](#telephone)

Phone number input with optional pattern validation.

```
'phone' => [
    'label'       => __( 'Phone Number', 'textdomain' ),
    'type'        => 'tel',
    'placeholder' => '+1 (555) 123-4567',
    'pattern'     => '[0-9]{3}-[0-9]{3}-[0-9]{4}', // Optional regex pattern
]
```

#### Password

[](#password)

Password input with show/hide toggle button.

```
'api_key' => [
    'label'       => __( 'API Key', 'textdomain' ),
    'type'        => 'password',
    'placeholder' => __( 'Enter your API key', 'textdomain' ),
]
```

---

### Multi-line Text Fields

[](#multi-line-text-fields)

#### Textarea

[](#textarea)

Multi-line text input.

```
'description' => [
    'label'       => __( 'Description', 'textdomain' ),
    'type'        => 'textarea',
    'rows'        => 5,
    'placeholder' => __( 'Enter a description...', 'textdomain' ),
]
```

#### WYSIWYG

[](#wysiwyg)

WordPress rich text editor (TinyMCE).

```
'content' => [
    'label' => __( 'Content', 'textdomain' ),
    'type'  => 'wysiwyg',
    'rows'  => 10,
]
```

#### Code

[](#code)

Code editor with syntax highlighting using WordPress CodeMirror.

```
'custom_css' => [
    'label'        => __( 'Custom CSS', 'textdomain' ),
    'type'         => 'code',
    'language'     => 'css',      // html, css, javascript, js, json, php, sql, xml, markdown, md
    'line_numbers' => true,
    'rows'         => 15,
]
```

**Supported Languages:**

- `html` / `htmlmixed`
- `css`
- `javascript` / `js`
- `json`
- `php`
- `sql`
- `xml`
- `markdown` / `md`

---

### Numeric Fields

[](#numeric-fields)

#### Number

[](#number)

Numeric input with optional constraints.

```
'quantity' => [
    'label'       => __( 'Quantity', 'textdomain' ),
    'type'        => 'number',
    'min'         => 0,
    'max'         => 100,
    'step'        => 1,
    'default'     => 0,
    'placeholder' => '0',
]
```

#### Range

[](#range)

Range slider input with live value display.

```
'volume' => [
    'label'   => __( 'Volume', 'textdomain' ),
    'type'    => 'range',
    'min'     => 0,
    'max'     => 100,
    'step'    => 5,
    'default' => 50,
    'unit'    => '%', // Optional unit suffix
]
```

#### Amount Type

[](#amount-type)

Combined numeric input with type selector (e.g., discount amount + percent/flat).

```
'discount' => [
    'label'         => __( 'Discount', 'textdomain' ),
    'type'          => 'amount_type',
    'type_meta_key' => 'discount_type', // Separate meta key for the type
    'type_options'  => [
        'percent' => '%',
        'flat'    => '$',
    ],
    'type_default'  => 'percent',
    'min'           => 0,
    'max'           => 100,
    'step'          => 0.01,
]
```

#### Dimensions

[](#dimensions)

Combined width × height input.

```
'image_size' => [
    'label'            => __( 'Image Size', 'textdomain' ),
    'type'             => 'dimensions',
    'dimension_labels' => [
        'width'  => __( 'Width', 'textdomain' ),
        'height' => __( 'Height', 'textdomain' ),
    ],
    'dimension_units'  => 'px', // Optional unit label
    'min'              => 0,
    'max'              => 4000,
    'step'             => 1,
]
```

---

### Date &amp; Time Fields

[](#date--time-fields)

#### Date

[](#date)

HTML5 date picker.

```
'start_date' => [
    'label' => __( 'Start Date', 'textdomain' ),
    'type'  => 'date',
]
```

#### Time

[](#time)

HTML5 time picker.

```
'opening_time' => [
    'label' => __( 'Opening Time', 'textdomain' ),
    'type'  => 'time',
]
```

#### DateTime

[](#datetime)

HTML5 date and time picker.

```
'event_datetime' => [
    'label' => __( 'Event Date & Time', 'textdomain' ),
    'type'  => 'datetime',
]
```

#### Date Range

[](#date-range)

Two date inputs for start and end dates.

```
'event_dates' => [
    'label'       => __( 'Event Dates', 'textdomain' ),
    'type'        => 'date_range',
    'start_label' => __( 'Start', 'textdomain' ),
    'end_label'   => __( 'End', 'textdomain' ),
]
```

**Value Structure:**

```
[
    'start' => '2024-01-15',
    'end'   => '2024-01-20',
]
```

#### Time Range

[](#time-range)

Two time inputs for start and end times.

```
'business_hours' => [
    'label'       => __( 'Business Hours', 'textdomain' ),
    'type'        => 'time_range',
    'start_label' => __( 'Opens', 'textdomain' ),
    'end_label'   => __( 'Closes', 'textdomain' ),
]
```

**Value Structure:**

```
[
    'start' => '09:00',
    'end'   => '17:00',
]
```

---

### Choice Fields

[](#choice-fields)

#### Select

[](#select)

Dropdown selection with static or dynamic options.

```
// Static options
'layout' => [
    'label'   => __( 'Layout', 'textdomain' ),
    'type'    => 'select',
    'default' => 'grid',
    'options' => [
        ''     => __( '— Select —', 'textdomain' ),
        'grid' => __( 'Grid', 'textdomain' ),
        'list' => __( 'List', 'textdomain' ),
    ],
]

// Multiple selection
'categories' => [
    'label'    => __( 'Categories', 'textdomain' ),
    'type'     => 'select',
    'multiple' => true,
    'options'  => [
        'tech'    => __( 'Technology', 'textdomain' ),
        'health'  => __( 'Health', 'textdomain' ),
        'finance' => __( 'Finance', 'textdomain' ),
    ],
]

// Dynamic options via callback
'parent_page' => [
    'label'   => __( 'Parent Page', 'textdomain' ),
    'type'    => 'select',
    'options' => function() {
        $pages = get_pages();
        $options = [ '' => __( '— None —', 'textdomain' ) ];
        foreach ( $pages as $page ) {
            $options[ $page->ID ] = $page->post_title;
        }
        return $options;
    },
]
```

#### Checkbox

[](#checkbox)

Boolean toggle checkbox.

```
'featured' => [
    'label'       => __( 'Featured', 'textdomain' ),
    'type'        => 'checkbox',
    'default'     => 0,
    'description' => __( 'Show in featured section.', 'textdomain' ),
]
```

#### Toggle

[](#toggle)

Visual toggle switch (alternative to checkbox).

```
'is_active' => [
    'label'       => __( 'Active', 'textdomain' ),
    'type'        => 'toggle',
    'default'     => 0,
    'on_label'    => __( 'On', 'textdomain' ),  // Optional
    'off_label'   => __( 'Off', 'textdomain' ), // Optional
    'description' => __( 'Enable this feature.', 'textdomain' ),
]
```

#### Radio

[](#radio)

Radio button group.

```
'priority' => [
    'label'   => __( 'Priority', 'textdomain' ),
    'type'    => 'radio',
    'default' => 'medium',
    'layout'  => 'horizontal', // or 'vertical' (default)
    'options' => [
        'low'    => __( 'Low', 'textdomain' ),
        'medium' => __( 'Medium', 'textdomain' ),
        'high'   => __( 'High', 'textdomain' ),
    ],
]
```

#### Button Group

[](#button-group)

Toggle button group for single or multiple selection.

```
// Single selection
'alignment' => [
    'label'   => __( 'Alignment', 'textdomain' ),
    'type'    => 'button_group',
    'default' => 'left',
    'options' => [
        'left'   => __( 'Left', 'textdomain' ),
        'center' => __( 'Center', 'textdomain' ),
        'right'  => __( 'Right', 'textdomain' ),
    ],
]

// Multiple selection
'features' => [
    'label'    => __( 'Features', 'textdomain' ),
    'type'     => 'button_group',
    'multiple' => true,
    'options'  => [
        'bold'      => __( 'Bold', 'textdomain' ),
        'italic'    => __( 'Italic', 'textdomain' ),
        'underline' => __( 'Underline', 'textdomain' ),
    ],
]
```

---

### Color Field

[](#color-field)

#### Color

[](#color)

WordPress color picker with optional default.

```
'brand_color' => [
    'label'   => __( 'Brand Color', 'textdomain' ),
    'type'    => 'color',
    'default' => '#3498db',
]
```

---

### Media Fields

[](#media-fields)

#### Image

[](#image)

Single image picker from WordPress media library.

```
'featured_image' => [
    'label'       => __( 'Featured Image', 'textdomain' ),
    'type'        => 'image',
    'button_text' => __( 'Select Image', 'textdomain' ),
]
```

**Stored Value:** Attachment ID (integer)

#### File

[](#file)

Single file picker from WordPress media library (stores attachment ID).

```
'download_file' => [
    'label'       => __( 'Download File', 'textdomain' ),
    'type'        => 'file',
    'button_text' => __( 'Select File', 'textdomain' ),
]
```

**Stored Value:** Attachment ID (integer)

#### File URL

[](#file-url)

Text input with media library button (stores URL, editable).

```
'external_file' => [
    'label'       => __( 'File URL', 'textdomain' ),
    'type'        => 'file_url',
    'button_text' => __( 'Browse', 'textdomain' ),
    'placeholder' => __( 'Enter URL or select from media library', 'textdomain' ),
]
```

**Stored Value:** URL string (editable)

#### Gallery

[](#gallery)

Multiple image picker with drag-and-drop reordering.

```
'gallery' => [
    'label'       => __( 'Gallery Images', 'textdomain' ),
    'type'        => 'gallery',
    'max_items'   => 10,  // 0 = unlimited
    'button_text' => __( 'Add Images', 'textdomain' ),
]
```

**Stored Value:** Array of attachment IDs

#### Link

[](#link)

Combined URL, title, and target fields.

```
'call_to_action' => [
    'label'       => __( 'Call to Action', 'textdomain' ),
    'type'        => 'link',
    'show_title'  => true,  // Show link text field
    'show_target' => true,  // Show "open in new tab" checkbox
]
```

**Value Structure:**

```
[
    'url'    => 'https://example.com',
    'title'  => 'Click Here',
    'target' => '_blank', // or ''
]
```

#### oEmbed

[](#oembed)

URL input with embedded preview for supported providers.

```
'video_embed' => [
    'label'       => __( 'Video URL', 'textdomain' ),
    'type'        => 'oembed',
    'placeholder' => __( 'Enter URL (YouTube, Vimeo, Twitter, etc.)', 'textdomain' ),
]
```

**Supports:** YouTube, Vimeo, Twitter, Instagram, Spotify, and other WordPress-supported oEmbed providers.

---

### Relational Fields (Static)

[](#relational-fields-static)

These fields query all available items on page load. Best for smaller datasets.

#### Post

[](#post)

Post/page/custom post type selector.

```
// Single selection
'related_post' => [
    'label'     => __( 'Related Post', 'textdomain' ),
    'type'      => 'post',
    'post_type' => 'post', // Can be array: ['post', 'page']
]

// Multiple selection as checkboxes
'related_posts' => [
    'label'     => __( 'Related Posts', 'textdomain' ),
    'type'      => 'post',
    'post_type' => 'post',
    'multiple'  => true,
    'display'   => 'checkbox', // or 'select'
]
```

#### User

[](#user)

WordPress user selector.

```
// All users
'author' => [
    'label' => __( 'Author', 'textdomain' ),
    'type'  => 'user',
]

// Filtered by role
'editor' => [
    'label'    => __( 'Editor', 'textdomain' ),
    'type'     => 'user',
    'role'     => ['editor', 'administrator'], // Can be string or array
    'multiple' => true,
]
```

#### Term

[](#term)

Taxonomy term selector.

```
'category' => [
    'label'    => __( 'Category', 'textdomain' ),
    'type'     => 'term',
    'taxonomy' => 'category',
]

'tags' => [
    'label'    => __( 'Tags', 'textdomain' ),
    'type'     => 'term',
    'taxonomy' => 'post_tag',
    'multiple' => true,
    'display'  => 'checkbox',
]
```

---

### Relational Fields (AJAX-Powered)

[](#relational-fields-ajax-powered)

These fields use Select2 with AJAX search. Best for large datasets.

#### Post AJAX

[](#post-ajax)

AJAX-powered post selector.

```
'related_product' => [
    'label'       => __( 'Related Product', 'textdomain' ),
    'type'        => 'post_ajax',
    'post_type'   => 'product', // Can be array: ['product', 'variation']
    'multiple'    => true,
    'placeholder' => __( 'Search products...', 'textdomain' ),
]
```

#### Taxonomy AJAX

[](#taxonomy-ajax)

AJAX-powered taxonomy term selector.

```
'product_categories' => [
    'label'       => __( 'Product Categories', 'textdomain' ),
    'type'        => 'taxonomy_ajax',
    'taxonomy'    => 'product_cat',
    'multiple'    => true,
    'placeholder' => __( 'Search categories...', 'textdomain' ),
]
```

#### User AJAX

[](#user-ajax)

AJAX-powered user selector.

```
'team_members' => [
    'label'       => __( 'Team Members', 'textdomain' ),
    'type'        => 'user_ajax',
    'role'        => ['author', 'editor'], // Optional role filter
    'multiple'    => true,
    'placeholder' => __( 'Search users...', 'textdomain' ),
]
```

#### AJAX (Custom Callback)

[](#ajax-custom-callback)

Custom AJAX-powered select with your own data source.

```
'country' => [
    'label'         => __( 'Country', 'textdomain' ),
    'type'          => 'ajax',
    'ajax_callback' => 'my_country_search_callback',
    'multiple'      => false,
    'placeholder'   => __( 'Search countries...', 'textdomain' ),
]

// Callback function
function my_country_search_callback( $search = '', $ids = null ) {
    $countries = [
        'US' => 'United States',
        'UK' => 'United Kingdom',
        'CA' => 'Canada',
        // ... more countries
    ];

    $results = [];

    // Hydration: return specific items by ID
    if ( $ids ) {
        foreach ( $ids as $id ) {
            if ( isset( $countries[ $id ] ) ) {
                $results[] = [
                    'value' => $id,
                    'label' => $countries[ $id ],
                ];
            }
        }
        return $results;
    }

    // Search: filter by search term
    foreach ( $countries as $code => $name ) {
        if ( empty( $search ) || stripos( $name, $search ) !== false ) {
            $results[] = [
                'value' => $code,
                'label' => $name,
            ];
        }
    }

    return $results;
}
```

---

### Complex Fields

[](#complex-fields)

#### Group

[](#group)

Static group of related fields stored as a single meta value.

```
'dimensions' => [
    'label'  => __( 'Dimensions', 'textdomain' ),
    'type'   => 'group',
    'fields' => [
        'width' => [
            'label' => __( 'Width', 'textdomain' ),
            'type'  => 'number',
            'min'   => 0,
        ],
        'height' => [
            'label' => __( 'Height', 'textdomain' ),
            'type'  => 'number',
            'min'   => 0,
        ],
        'depth' => [
            'label' => __( 'Depth', 'textdomain' ),
            'type'  => 'number',
            'min'   => 0,
        ],
    ],
]
```

**Value Structure:**

```
[
    'width'  => 100,
    'height' => 50,
    'depth'  => 25,
]
```

#### Repeater

[](#repeater)

Dynamic repeatable field groups with drag-and-drop reordering.

```
'features' => [
    'label'           => __( 'Features', 'textdomain' ),
    'type'            => 'repeater',
    'button_label'    => __( 'Add Feature', 'textdomain' ),
    'max_items'       => 10,    // 0 = unlimited
    'min_items'       => 0,
    'collapsed'       => true,  // Start rows collapsed
    'layout'          => 'vertical', // 'vertical', 'horizontal', or 'table'
    'row_title'       => __( 'Feature {index}', 'textdomain' ), // Dynamic title with {index}
    'row_title_field' => 'title', // Use field value as title
    'fields'          => [
        'icon' => [
            'label' => __( 'Icon', 'textdomain' ),
            'type'  => 'text',
        ],
        'title' => [
            'label' => __( 'Title', 'textdomain' ),
            'type'  => 'text',
        ],
        'description' => [
            'label' => __( 'Description', 'textdomain' ),
            'type'  => 'textarea',
            'rows'  => 2,
        ],
    ],
]
```

**Repeater Layouts:**

- `vertical` (default): Fields stacked vertically in collapsible rows
- `horizontal`: Fields displayed horizontally in each row
- `table`: Compact table layout with column headers

**Row Title Placeholders:**

- `{index}`: Replaced with row number (1, 2, 3...)
- `{value}`: Replaced with value from `row_title_field`

**Value Structure:**

```
[
    [
        'icon'        => 'star',
        'title'       => 'Feature 1',
        'description' => 'Description of feature 1',
    ],
    [
        'icon'        => 'heart',
        'title'       => 'Feature 2',
        'description' => 'Description of feature 2',
    ],
]
```

---

Conditional Logic (show\_when)
------------------------------

[](#conditional-logic-show_when)

Fields can be shown or hidden based on the values of other fields. This is perfect for creating dynamic forms where certain options only appear when relevant.

### Simple Shorthand Syntax

[](#simple-shorthand-syntax)

```
'custom_url' => [
    'label'     => __( 'Custom URL', 'textdomain' ),
    'type'      => 'url',
    'show_when' => [ 'use_external_link' => 1 ],
]
```

### Explicit Syntax with Operators

[](#explicit-syntax-with-operators)

```
'discount_code' => [
    'label'     => __( 'Discount Code', 'textdomain' ),
    'type'      => 'text',
    'show_when' => [
        'field'    => 'enable_discount',
        'operator' => '==',
        'value'    => 1,
    ],
]
```

### Multiple Conditions (AND Logic)

[](#multiple-conditions-and-logic)

All conditions must be true for the field to show:

```
'shipping_notes' => [
    'label'     => __( 'Shipping Notes', 'textdomain' ),
    'type'      => 'textarea',
    'show_when' => [
        [ 'field' => 'product_type', 'value' => 'physical' ],
        [ 'field' => 'requires_shipping', 'value' => 1 ],
    ],
]
```

### Available Operators

[](#available-operators)

OperatorDescription`==` or `=`Equal (loose comparison)`===`Strictly equal`!=` or ``Not equal`!==`Strictly not equal`>`Greater than`>=`Greater than or equal` __( 'Product Options', 'textdomain' ),
    'post_types' => 'product',
    'fields'     => [
        'product_type' => [
            'label'   => __( 'Product Type', 'textdomain' ),
            'type'    => 'select',
            'options' => [
                'physical' => __( 'Physical Product', 'textdomain' ),
                'digital'  => __( 'Digital Product', 'textdomain' ),
                'service'  => __( 'Service', 'textdomain' ),
            ],
        ],
        // Only show for physical products
        'weight' => [
            'label'     => __( 'Weight (kg)', 'textdomain' ),
            'type'      => 'number',
            'step'      => 0.01,
            'show_when' => [ 'product_type' => 'physical' ],
        ],
        'dimensions' => [
            'label'     => __( 'Dimensions', 'textdomain' ),
            'type'      => 'group',
            'show_when' => [ 'product_type' => 'physical' ],
            'fields'    => [
                'width'  => [ 'label' => 'Width', 'type' => 'number' ],
                'height' => [ 'label' => 'Height', 'type' => 'number' ],
                'depth'  => [ 'label' => 'Depth', 'type' => 'number' ],
            ],
        ],
        // Only show for digital products
        'download_file' => [
            'label'     => __( 'Download File', 'textdomain' ),
            'type'      => 'file',
            'show_when' => [ 'product_type' => 'digital' ],
        ],
        'download_limit' => [
            'label'     => __( 'Download Limit', 'textdomain' ),
            'type'      => 'number',
            'show_when' => [ 'product_type' => 'digital' ],
        ],
        // Only show for services
        'duration' => [
            'label'     => __( 'Duration (hours)', 'textdomain' ),
            'type'      => 'number',
            'show_when' => [ 'product_type' => 'service' ],
        ],
    ],
] );
```

### Conditional Fields in Repeaters

[](#conditional-fields-in-repeaters)

Conditional logic also works within repeater fields:

```
register_post_fields( 'rewards', [
    'title'      => __( 'Rewards', 'textdomain' ),
    'post_types' => 'campaign',
    'fields'     => [
        'rewards' => [
            'label'        => __( 'Rewards', 'textdomain' ),
            'type'         => 'repeater',
            'button_label' => __( 'Add Reward', 'textdomain' ),
            'fields'       => [
                'reward_type' => [
                    'label'   => __( 'Reward Type', 'textdomain' ),
                    'type'    => 'select',
                    'options' => [
                        ''           => __( '— Select —', 'textdomain' ),
                        'send_email' => __( 'Send Email', 'textdomain' ),
                        'discount'   => __( 'Offer Discount', 'textdomain' ),
                        'add_points' => __( 'Add Points', 'textdomain' ),
                    ],
                ],
                'email_subject' => [
                    'label'     => __( 'Email Subject', 'textdomain' ),
                    'type'      => 'text',
                    'show_when' => [ 'reward_type' => 'send_email' ],
                ],
                'email_body' => [
                    'label'     => __( 'Email Body', 'textdomain' ),
                    'type'      => 'textarea',
                    'show_when' => [ 'reward_type' => 'send_email' ],
                ],
                'discount_amount' => [
                    'label'     => __( 'Discount Amount', 'textdomain' ),
                    'type'      => 'number',
                    'show_when' => [ 'reward_type' => 'discount' ],
                ],
                'points' => [
                    'label'     => __( 'Points to Add', 'textdomain' ),
                    'type'      => 'number',
                    'show_when' => [ 'reward_type' => 'add_points' ],
                ],
            ],
        ],
    ],
] );
```

---

Metabox Configuration Options
-----------------------------

[](#metabox-configuration-options)

OptionTypeDefaultDescription`title`string`'Additional Information'`Metabox title displayed in admin`post_types`string|array`['post']`Post type(s) to register for`context`string`'normal'`Metabox position: `normal`, `side`, `advanced``priority`string`'high'`Metabox priority: `high`, `core`, `default`, `low``prefix`string`''`Prefix for all meta keys`capability`string`'edit_posts'`Required capability to view/edit`fields`array`[]`Array of field configurations`full_width`bool`false`Use full width layout---

Field Configuration Options
---------------------------

[](#field-configuration-options)

### Common Options (All Fields)

[](#common-options-all-fields)

OptionTypeDefaultDescription`label`string`''`Field label text`type`string`'text'`Field type`description`string`''`Help text displayed below the field`tooltip`string`''`Tooltip text shown on hover`default`mixed`''`Default value`placeholder`string`''`Placeholder text for text inputs`show_when`array`[]`Conditional visibility rules`sanitize_callback`callable`null`Custom sanitization function`capability`string`'edit_posts'`Required capability to view/edit`show_in_rest`bool`true`Expose field via REST API### Choice Field Options

[](#choice-field-options)

OptionTypeDefaultDescription`options`array|callable`[]`Options for select/radio/button\_group`multiple`bool`false`Allow multiple selections`display`string`'select'`Display as `'select'` or `'checkbox'` for multiple`layout`string`'vertical'`Layout for radio: `'vertical'` or `'horizontal'`### Number Field Options

[](#number-field-options)

OptionTypeDefaultDescription`min`int|float`null`Minimum value`max`int|float`null`Maximum value`step`int|float`null`Step increment`unit`string`''`Unit suffix for range fields### Text Area Options

[](#text-area-options)

OptionTypeDefaultDescription`rows`int`5`Number of rows for textarea/wysiwyg### Code Editor Options

[](#code-editor-options)

OptionTypeDefaultDescription`language`string`'html'`Syntax highlighting language`line_numbers`bool`true`Show line numbers### Media Field Options

[](#media-field-options)

OptionTypeDefaultDescription`button_text`string`''`Custom button text`max_items`int`0`Maximum items for gallery (0=unlimited)`mime_types`array`[]`Allowed MIME types### Link Field Options

[](#link-field-options)

OptionTypeDefaultDescription`show_title`bool`true`Show link text field`show_target`bool`true`Show "open in new tab" checkbox### Relational Field Options

[](#relational-field-options)

OptionTypeDefaultDescription`post_type`string|array`'post'`Post type(s) for post fields`taxonomy`string`'category'`Taxonomy for term fields`role`string|array`[]`User role(s) for user fields### Amount Type Options

[](#amount-type-options)

OptionTypeDefaultDescription`type_meta_key`string`''`Meta key for storing the type`type_options`array`[]`Type options (e.g., `['percent' => '%', 'flat' => '$']`)`type_default`string`''`Default type value### Dimensions Options

[](#dimensions-options)

OptionTypeDefaultDescription`dimension_labels`array`[]`Labels: `['width' => 'W', 'height' => 'H']``dimension_units`string`''`Unit label (e.g., `'px'`, `'cm'`)### Date/Time Range Options

[](#datetime-range-options)

OptionTypeDefaultDescription`start_label`string`'Start'`Label for start field`end_label`string`'End'`Label for end field### Group/Repeater Options

[](#grouprepeater-options)

OptionTypeDefaultDescription`fields`array`[]`Nested field configurations`button_label`string`''`Add row button text (repeater)`max_items`int`0`Maximum rows (0=unlimited)`min_items`int`0`Minimum rows`collapsed`bool`false`Start rows collapsed`layout`string`'vertical'`Layout: `'vertical'`, `'horizontal'`, `'table'``row_title`string`''`Row title template with `{index}` placeholder`row_title_field`string`''`Field key to use as row title`width`string`''`Field width in repeater (e.g., `'25%'`)---

Retrieving Field Values
-----------------------

[](#retrieving-field-values)

### Standard Method

[](#standard-method)

```
$value = get_post_meta( $post_id, 'price', true );
```

### With Default Fallback

[](#with-default-fallback)

```
$value = get_post_field_value( $post_id, 'price', 'product_info' );
```

### Repeater Values

[](#repeater-values)

```
$features = get_post_meta( $post_id, 'features', true );

if ( ! empty( $features ) && is_array( $features ) ) {
    foreach ( $features as $feature ) {
        echo $feature['icon'];
        echo $feature['title'];
        echo $feature['description'];
    }
}
```

### Group Values

[](#group-values)

```
$dimensions = get_post_meta( $post_id, 'dimensions', true );

if ( ! empty( $dimensions ) ) {
    echo 'Width: ' . $dimensions['width'];
    echo 'Height: ' . $dimensions['height'];
    echo 'Depth: ' . $dimensions['depth'];
}
```

### Link Values

[](#link-values)

```
$link = get_post_meta( $post_id, 'call_to_action', true );

if ( ! empty( $link['url'] ) ) {
    $target = $link['target'] === '_blank' ? ' target="_blank" rel="noopener"' : '';
    echo '';
    echo esc_html( $link['title'] ?: 'Learn More' );
    echo '';
}
```

### Amount Type Values

[](#amount-type-values)

```
$discount_amount = get_post_meta( $post_id, 'discount', true );
$discount_type   = get_post_meta( $post_id, 'discount_type', true );

if ( $discount_amount ) {
    if ( $discount_type === 'percent' ) {
        echo $discount_amount . '% off';
    } else {
        echo '$' . $discount_amount . ' off';
    }
}
```

---

Helper Functions
----------------

[](#helper-functions)

### register\_post\_fields()

[](#register_post_fields)

Register a new metabox with fields.

```
$metabox = register_post_fields( 'metabox_id', $config );
```

### get\_post\_field\_value()

[](#get_post_field_value)

Get a field value with default fallback.

```
$value = get_post_field_value( $post_id, 'meta_key', 'metabox_id' );
```

### get\_post\_fields()

[](#get_post_fields)

Get all field configurations for a metabox.

```
$fields = get_post_fields( 'metabox_id' );
```

### get\_post\_field\_config()

[](#get_post_field_config)

Get configuration for a specific field.

```
$config = get_post_field_config( 'metabox_id', 'meta_key' );
```

### get\_all\_post\_field\_groups()

[](#get_all_post_field_groups)

Get all registered metaboxes.

```
$groups = get_all_post_field_groups();
```

---

REST API
--------

[](#rest-api)

All registered fields are automatically available via the REST API:

```
GET /wp-json/wp/v2/product/123

```

Response includes:

```
{
  "id": 123,
  "title": {
    "rendered": "Product Name"
  },
  "meta": {
    "sku": "PRD-001",
    "price": 29.99,
    "features": [
      {
        "icon": "dashicons-star",
        "title": "Feature 1",
        "description": "..."
      }
    ],
    "dimensions": {
      "width": 100,
      "height": 50,
      "depth": 25
    }
  }
}
```

---

Field Types Quick Reference
---------------------------

[](#field-types-quick-reference)

TypeDescriptionStored Value`text`Single-line text inputstring`url`URL input with validationstring`email`Email input with validationstring`tel`Phone number inputstring`password`Password with show/hide togglestring`textarea`Multi-line text inputstring`wysiwyg`Rich text editorstring (HTML)`code`Code editor with syntax highlightingstring`number`Numeric inputint|float`range`Range sliderint|float`amount_type`Number + type selectorfloat (+ separate type meta)`dimensions`Width × heightarray`date`Date pickerstring (Y-m-d)`time`Time pickerstring (H:i)`datetime`Date and time pickerstring`date_range`Start and end datesarray`time_range`Start and end timesarray`color`Color pickerstring (hex)`select`Dropdown selectionstring|array`checkbox`Boolean toggleint (0|1)`toggle`Visual toggle switchint (0|1)`radio`Radio button groupstring`button_group`Toggle button groupstring|array`image`Single image pickerint (attachment ID)`file`Single file pickerint (attachment ID)`file_url`File URL inputstring (URL)`gallery`Multiple imagesarray (attachment IDs)`link`URL + title + targetarray`oembed`Embeddable URLstring (URL)`post`Post selector (static)int|array`user`User selector (static)int|array`term`Term selector (static)int|array`post_ajax`Post selector (AJAX)int|array`taxonomy_ajax`Term selector (AJAX)int|array`user_ajax`User selector (AJAX)int|array`ajax`Custom AJAX selectormixed`group`Static field grouparray`repeater`Dynamic field grouparray---

Nested Field Support
--------------------

[](#nested-field-support)

The following field types are supported inside **groups** and **repeaters**:

SupportedField Type✅text, url, email, tel✅textarea✅number✅select✅checkbox✅radio✅button\_group✅range✅image✅file✅file\_url✅user✅ajax✅post\_ajax✅taxonomy\_ajax✅user\_ajax❌wysiwyg❌gallery❌group (no nesting)❌repeater (no nesting)---

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

GPL-2.0-or-later

Author
------

[](#author)

David Sherlock - [ArrayPress](https://arraypress.com/)

###  Health Score

21

—

LowBetter than 18% of packages

Maintenance59

Moderate activity, may be stable

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/cd6eb8aff0903d87eb674d1ba3c5f3653899c0d7661504eb0deb7798ed86b643?d=identicon)[arraypress](/maintainers/arraypress)

---

Top Contributors

[![arraypress](https://avatars.githubusercontent.com/u/22668877?v=4)](https://github.com/arraypress "arraypress (37 commits)")

### Embed Badge

![Health badge](/badges/arraypress-wp-register-post-fields/health.svg)

```
[![Health](https://phpackages.com/badges/arraypress-wp-register-post-fields/health.svg)](https://phpackages.com/packages/arraypress-wp-register-post-fields)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
