This document provides guidance on how to extend the WPify Custom Fields plugin with your own custom field types.
The WPify Custom Fields plugin is designed to be highly extensible, allowing developers to create and register custom field types. To create a custom field type, you need to:
- Create a PHP filter for sanitization
- Specify the WordPress data type
- Define default values
- Create a JavaScript component for the field's UI
- Register the field type with the WordPress filter system
Each field type requires a filter for sanitizing values. The filter follows the naming convention: wpifycf_sanitize_{type}.
add_filter('wpifycf_sanitize_my_custom_field', function($sanitized_value, $original_value, $item) {
// Custom sanitization logic
return $sanitized_value;
}, 10, 3);The filter receives three parameters:
$sanitized_value: The pre-sanitized value (default WordPress sanitization)$original_value: The raw input value$item: The complete field configuration array
Each field type must be mapped to a WordPress data type via the wpifycf_wp_type_{type} filter:
add_filter('wpifycf_wp_type_my_custom_field', function($type, $item) {
// Return one of: 'integer', 'number', 'boolean', 'object', 'array', 'string'
return 'string';
}, 10, 2);Available WordPress data types:
integer: Whole numbers (no decimals)number: Numeric values (with decimals)boolean: True/false valuesobject: Objects/associative arraysarray: Indexed arrays/listsstring: Text values
Define a default value for your field type using the wpifycf_default_value_{type} filter:
add_filter('wpifycf_default_value_my_custom_field', function($default_value, $item) {
// Return the default value for this field type
return '';
}, 10, 2);The Post, Multi Post, and Link fields fetch their options from the posts REST
endpoint (see REST API). Two filters shape what it returns.
Controls whether a posts query searches WooCommerce SKUs in addition to titles.
It is enabled by default when the queried post types include product or
product_variation and WooCommerce is active.
add_filter(
'wpifycf_search_posts_by_sku',
function ( $enabled, $post_types, $args ) {
// Extend the SKU search to a custom post type that stores its code
// in the WooCommerce product lookup table.
if ( in_array( 'my_catalog_item', $post_types, true ) ) {
return true;
}
return $enabled;
},
10,
3
);Filters the data of every post returned by the endpoint, so custom field components can receive extra information.
add_filter(
'wpifycf_post_data',
function ( $data, $post, $args ) {
$data['author_name'] = get_the_author_meta( 'display_name', $post->post_author );
return $data;
},
10,
3
);Create a React component that represents your field type:
import { useCallback } from 'react';
import clsx from 'clsx';
import { addFilter } from '@wordpress/hooks';
import { checkValidityStringType } from '@/helpers/validators';
export function MyCustomField({
id,
htmlId,
onChange,
value = '',
attributes = {},
className,
disabled = false,
fieldPath,
allValues = {},
getValue,
}) {
const handleChange = useCallback(event => onChange(event.target.value), [onChange]);
return (
<div
className={clsx('wpifycf-field-my-custom-field', `wpifycf-field-my-custom-field--${id}`, attributes.class, className)}
>
{/* Your field implementation */}
<input
type="text"
id={htmlId}
onChange={handleChange}
value={value}
disabled={disabled}
{...attributes}
/>
</div>
);
}Every field component receives the following props:
id(string) — The field's unique identifier.htmlId(string) — The HTMLidattribute for the input element.onChange(function) — Callback to update the field's value.value— The current field value.attributes(object) — Additional HTML attributes from the field definition.className(string) — Additional CSS class name.disabled(boolean) — Whether the field is disabled.fieldPath(string) — The current field's path in the form hierarchy. It identifies the field's position, which is especially useful for nested fields in groups or repeaters. The path uses dot notation (e.g.,parent_group.child_field) and array indices for repeater items (e.g.,multi_group[0].field_name). This property is used internally for relative path resolution and accessing sibling/parent field values.allValues(object) — An object containing all current form field values, where keys are field IDs. This allows field components to access any other field's value in the form.getValue(function) — A helper function to access other field values using path syntax. The function signature isgetValue(path: string): any. The path syntax supports:- Dot notation for nested fields:
parent.child - Relative references using hash:
#(parent),##(grandparent) - Array bracket notation:
multi_field[0] - Combinations:
#.sibling_field[0].name
- Dot notation for nested fields:
setTitle(function) — A callback prop(title: string) => voidthat lets a field report its human-readable title or summary to a parent container. This is used byMultiGroupto display collapsed row headers based on the field's current value. It is not configurable from PHP — it is an internal React prop passed by parent components. Use theuseFieldTitle(setTitle, titleValue)helper hook from@/helpers/hooksto callsetTitlereactively whenever the field's display value changes.
The fieldPath, allValues, and getValue props are useful for building fields that depend on other field values, such as dependent dropdowns or dynamically computed values.
Add a validation method to your component:
// You can use existing validators from helpers/validators.js
// or create your own validation function
MyCustomField.checkValidity = checkValidityStringType;Register your field type using WordPress filters:
// Register the field type
addFilter('wpifycf_field_my_custom_field', 'wpify_custom_fields', () => MyCustomField);Make sure your JavaScript file is loaded and registered with WordPress:
wp_enqueue_script(
'my-custom-fields',
plugin_dir_url(__FILE__) . 'js/my-custom-fields.js',
['wp-hooks', 'wpifycf-custom-fields'], // Dependencies
'1.0.0',
true
);Here's a complete example of creating a custom "Rating" field type:
/**
* Register sanitization for the rating field
*/
add_filter('wpifycf_sanitize_rating', function($sanitized_value, $original_value, $item) {
// Ensure rating is between 0 and 5
$rating = intval($original_value);
return min(5, max(0, $rating));
}, 10, 3);
/**
* Register WordPress data type for the rating field
*/
add_filter('wpifycf_wp_type_rating', function($type, $item) {
return 'integer';
}, 10, 2);
/**
* Register default value for the rating field
*/
add_filter('wpifycf_default_value_rating', function($default_value, $item) {
return 0;
}, 10, 2);// rating.js
import { useCallback } from 'react';
import clsx from 'clsx';
import { addFilter } from '@wordpress/hooks';
import { checkValidityNumberType } from '@/helpers/validators';
export function Rating({
id,
htmlId,
onChange,
value = 0,
attributes = {},
className,
disabled = false,
}) {
const handleChange = useCallback(event => {
const newValue = parseInt(event.target.value, 10);
onChange(newValue);
}, [onChange]);
// Create five star buttons
const stars = [];
for (let i = 1; i <= 5; i++) {
stars.push(
<button
key={i}
type="button"
className={clsx(
'wpifycf-rating-star',
i <= value && 'wpifycf-rating-star--active'
)}
onClick={() => onChange(i)}
disabled={disabled}
>
�
</button>
);
}
return (
<div
className={clsx('wpifycf-field-rating', `wpifycf-field-rating--${id}`, attributes.class, className)}
>
<input
type="hidden"
id={htmlId}
value={value}
{...attributes}
/>
<div className="wpifycf-rating-stars">
{stars}
</div>
</div>
);
}
// Use number type validation
Rating.checkValidity = checkValidityNumberType;
// Register field type
addFilter('wpifycf_field_rating', 'wpify_custom_fields', () => Rating);Once registered, you can use your custom field type in any integration:
$custom_fields = new \Wpify\CustomFields\CustomFields();
$metabox = $custom_fields->create_metabox([
'id' => 'product_review',
'title' => 'Product Review',
'post_types' => ['product'],
'items' => [
[
'id' => 'rating',
'type' => 'rating', // Your custom field type
'label' => 'Product Rating',
'description' => 'Rate this product from 1 to 5 stars',
],
// Other fields...
],
]);If you want to create a multi-version of your field (that accepts multiple values), you can leverage the existing multi-field framework:
// Set the WordPress type for the multi-version
add_filter('wpifycf_wp_type_multi_rating', function($type, $item) {
return 'array';
}, 10, 2);
// Set default value
add_filter('wpifycf_default_value_multi_rating', function($default_value, $item) {
return [];
}, 10, 2);
// Sanitization is handled automatically through the multi_ prefixThen create a JavaScript file that imports your base field and the MultiField component:
// multi-rating.js
import { addFilter } from '@wordpress/hooks';
import { MultiField } from '@/components/MultiField';
import { Rating } from './rating';
import { checkValidityMultiFieldType } from '@/helpers/validators';
// Create the multi-field component
function MultiRating(props) {
return <MultiField {...props} itemType="rating" />;
}
// Set validation
MultiRating.checkValidity = checkValidityMultiFieldType('rating');
// Register field type
addFilter('wpifycf_field_multi_rating', 'wpify_custom_fields', () => MultiRating);-
Naming Conventions:
- Use lowercase with underscores for field type identifiers
- Use PascalCase for JavaScript components
-
Validation:
- Always implement validation to ensure data integrity
- Use existing validation helpers when possible
-
CSS Naming:
- Follow the plugin's CSS naming:
wpifycf-field-{type} - Add modifiers with double dashes:
wpifycf-field-{type}--modifier
- Follow the plugin's CSS naming:
-
Security:
- Always sanitize input values
- Include proper escaping when outputting values
-
Documentation:
- Add PHPDoc comments to your filters
- Document expected input and output formats
- Field not showing up: Ensure your JavaScript is properly enqueued and that it depends on the main plugin script
- Validation errors: Check browser console for JavaScript errors
- Sanitization issues: Verify that your PHP filter is correctly registered and follows the naming convention