arraypress / wp-register-user-fields
Lightweight library for registering custom fields on WordPress user profile screens.
Installs: 8
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/arraypress/wp-register-user-fields
Requires
- php: >=7.4
This package is auto-updated.
Last update: 2025-12-04 21:02:54 UTC
README
A lightweight library for registering custom fields on WordPress user profile and Add New User screens. This library provides a clean, simple API for adding common field types to user profiles without JavaScript dependencies or complex configuration.
Features
- Simple API: Register custom user fields with minimal code
- Multiple Field Types: Text, textarea, number, select, checkbox, URL, and email
- Automatic Saving: Fields are automatically saved to user meta
- Smart Sanitization: Each field type has appropriate default sanitization
- Dynamic Options: Select fields support callable options for dynamic data
- Section Grouping: Group related fields under custom section headings
- Add New User Support: Fields appear on the Add New User screen
- Permission Control: Control field visibility based on user capabilities
- No JavaScript: Pure PHP implementation with WordPress-native markup
- Lightweight: Single class, minimal footprint
Requirements
- PHP 7.4 or higher
- WordPress 5.0 or higher
Installation
Install via Composer:
composer require arraypress/wp-register-user-fields
Basic Usage
Simple Text Fields
register_user_fields( [ 'company' => [ 'label' => __( 'Company', 'textdomain' ), 'type' => 'text', 'description' => __( 'Your company or organization name.', 'textdomain' ), ], 'job_title' => [ 'label' => __( 'Job Title', 'textdomain' ), 'type' => 'text', 'placeholder' => __( 'e.g., Senior Developer', 'textdomain' ), ], ] );
Custom Section Title
register_user_fields( [ 'department' => [ 'label' => __( 'Department', 'textdomain' ), 'type' => 'select', 'options' => [ '' => __( '— Select —', 'textdomain' ), 'sales' => __( 'Sales', 'textdomain' ), 'support' => __( 'Support', 'textdomain' ), 'dev' => __( 'Development', 'textdomain' ), 'marketing' => __( 'Marketing', 'textdomain' ), ], ], 'employee_id' => [ 'label' => __( 'Employee ID', 'textdomain' ), 'type' => 'text', ], ], __( 'Employee Information', 'textdomain' ) );
Multiple Field Groups
Register multiple groups with different section titles:
// Contact information register_user_fields( [ 'phone' => [ 'label' => __( 'Phone', 'textdomain' ), 'type' => 'text', 'placeholder' => '+1 (555) 000-0000', ], 'extension' => [ 'label' => __( 'Extension', 'textdomain' ), 'type' => 'number', 'min' => 100, 'max' => 9999, ], ], __( 'Contact Information', 'textdomain' ) ); // Social profiles register_user_fields( [ 'linkedin' => [ 'label' => __( 'LinkedIn URL', 'textdomain' ), 'type' => 'url', 'placeholder' => 'https://linkedin.com/in/username', ], 'twitter' => [ 'label' => __( 'Twitter/X Handle', 'textdomain' ), 'type' => 'text', 'placeholder' => '@username', ], ], __( 'Social Profiles', 'textdomain' ) );
Select with Dynamic Options
register_user_fields( [ 'manager_id' => [ 'label' => __( 'Reports To', 'textdomain' ), 'type' => 'select', 'description' => __( 'Select your direct manager.', 'textdomain' ), 'options' => function() { $managers = get_users( [ 'role__in' => [ 'administrator', 'editor' ], 'orderby' => 'display_name', ] ); $options = [ '' => __( '— Select Manager —', 'textdomain' ) ]; foreach ( $managers as $manager ) { $options[ $manager->ID ] = $manager->display_name; } return $options; }, ], ] );
Field Types
Text
Standard single-line text input.
'field_name' => [ 'label' => __( 'Field Label', 'textdomain' ), 'type' => 'text', 'placeholder' => __( 'Placeholder text...', 'textdomain' ), 'default' => '', ]
Textarea
Multi-line text input.
'bio' => [ 'label' => __( 'Biography', 'textdomain' ), 'type' => 'textarea', 'rows' => 5, 'placeholder' => __( 'Tell us about yourself...', 'textdomain' ), ]
Number
Numeric input with optional constraints.
'years_experience' => [ 'label' => __( 'Years of Experience', 'textdomain' ), 'type' => 'number', 'min' => 0, 'max' => 50, 'step' => 1, 'default' => 0, ]
For decimal values, set a decimal step:
'hourly_rate' => [ 'label' => __( 'Hourly Rate', 'textdomain' ), 'type' => 'number', 'min' => 0, 'step' => 0.01, ]
Select
Dropdown selection with static or dynamic options.
// Static options 'status' => [ 'label' => __( 'Status', 'textdomain' ), 'type' => 'select', 'default' => 'active', 'options' => [ 'active' => __( 'Active', 'textdomain' ), 'inactive' => __( 'Inactive', 'textdomain' ), 'pending' => __( 'Pending', 'textdomain' ), ], ] // Dynamic options via callback 'location' => [ 'label' => __( 'Office Location', 'textdomain' ), 'type' => 'select', 'options' => function() { // Could query from database, API, etc. return [ '' => __( '— Select —', 'textdomain' ), 'nyc' => __( 'New York', 'textdomain' ), 'london' => __( 'London', 'textdomain' ), 'tokyo' => __( 'Tokyo', 'textdomain' ), ]; }, ]
Checkbox
Boolean toggle field.
'subscribe_newsletter' => [ 'label' => __( 'Subscribe to Newsletter', 'textdomain' ), 'type' => 'checkbox', 'default' => 0, 'description' => __( 'Receive weekly updates via email.', 'textdomain' ), ]
URL
Text input with URL validation.
'website' => [ 'label' => __( 'Personal Website', 'textdomain' ), 'type' => 'url', 'placeholder' => 'https://example.com', ]
Text input with email validation.
'alternate_email' => [ 'label' => __( 'Alternate Email', 'textdomain' ), 'type' => 'email', 'placeholder' => 'alternate@example.com', 'description' => __( 'Secondary contact email address.', 'textdomain' ), ]
Field Configuration Options
Each field accepts the following configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
label |
string | '' |
Field label text |
type |
string | 'text' |
Field type |
description |
string | '' |
Help text displayed below the field |
default |
mixed | '' |
Default value for new users |
placeholder |
string | '' |
Placeholder text for text inputs |
options |
array|callable | [] |
Options for select fields |
min |
int|float | null |
Minimum value for number fields |
max |
int|float | null |
Maximum value for number fields |
step |
int|float | null |
Step increment for number fields |
rows |
int | 5 |
Number of rows for textarea fields |
sanitize_callback |
callable | null |
Custom sanitization function |
capability |
string | 'edit_users' |
Required capability to view/edit the field |
show_in_admin |
bool | true |
Show field when editing other users |
show_in_profile |
bool | true |
Show field on own profile |
Retrieving Field Values
Standard Method
Use WordPress's built-in function:
$company = get_user_meta( $user_id, 'company', true );
With Default Fallback
Use the helper function to automatically fall back to registered defaults:
$department = get_user_field_value( $user_id, 'department' );
Get All Registered Fields
$all_fields = get_all_user_fields(); foreach ( $all_fields as $group_id => $fields ) { foreach ( $fields as $meta_key => $config ) { echo $config['label'] . ': ' . get_user_meta( $user_id, $meta_key, true ); } }
Get Field Configuration
$config = get_user_field_config( 'department' ); if ( $config ) { echo 'Label: ' . $config['label']; echo 'Type: ' . $config['type']; }
Custom Sanitization
Override the default sanitization for any field:
register_user_fields( [ 'custom_html' => [ 'label' => __( 'Custom Bio', 'textdomain' ), 'type' => 'textarea', 'sanitize_callback' => function( $value ) { return wp_kses( $value, [ 'p' => [], 'br' => [], 'strong' => [], 'em' => [], 'a' => [ 'href' => [], 'title' => [] ], ] ); }, ], ] );
Permission Control
Control field visibility based on user capabilities:
register_user_fields( [ 'salary' => [ 'label' => __( 'Salary', 'textdomain' ), 'type' => 'number', 'capability' => 'manage_options', // Only administrators 'description' => __( 'Annual salary (admin only).', 'textdomain' ), ], 'notes' => [ 'label' => __( 'HR Notes', 'textdomain' ), 'type' => 'textarea', 'capability' => 'edit_users', 'show_in_profile' => false, // Don't show to user on their own profile ], ] );
Complete Example
// Employee information register_user_fields( [ 'employee_id' => [ 'label' => __( 'Employee ID', 'textdomain' ), 'type' => 'text', 'placeholder' => 'EMP-0000', ], 'department' => [ 'label' => __( 'Department', 'textdomain' ), 'type' => 'select', 'options' => [ '' => __( '— Select —', 'textdomain' ), 'sales' => __( 'Sales', 'textdomain' ), 'support' => __( 'Support', 'textdomain' ), 'dev' => __( 'Development', 'textdomain' ), 'hr' => __( 'Human Resources', 'textdomain' ), ], ], 'start_date' => [ 'label' => __( 'Start Date', 'textdomain' ), 'type' => 'text', 'placeholder' => 'YYYY-MM-DD', ], 'manager_id' => [ 'label' => __( 'Manager', 'textdomain' ), 'type' => 'select', 'options' => function() { $users = get_users( [ 'role' => 'administrator' ] ); $options = [ '' => __( '— None —', 'textdomain' ) ]; foreach ( $users as $user ) { $options[ $user->ID ] = $user->display_name; } return $options; }, ], 'remote_worker' => [ 'label' => __( 'Remote Worker', 'textdomain' ), 'type' => 'checkbox', 'description' => __( 'This employee works remotely.', 'textdomain' ), ], ], __( 'Employee Information', 'textdomain' ) ); // Contact details register_user_fields( [ 'phone' => [ 'label' => __( 'Phone', 'textdomain' ), 'type' => 'text', 'placeholder' => '+1 (555) 000-0000', ], 'extension' => [ 'label' => __( 'Extension', 'textdomain' ), 'type' => 'number', 'min' => 100, 'max' => 9999, ], 'slack_handle' => [ 'label' => __( 'Slack Handle', 'textdomain' ), 'type' => 'text', 'placeholder' => '@username', ], ], __( 'Contact Information', 'textdomain' ) ); // Usage in templates $user_id = get_current_user_id(); $department = get_user_field_value( $user_id, 'department' ); $is_remote = get_user_meta( $user_id, 'remote_worker', true ); $employee_id = get_user_meta( $user_id, 'employee_id', true );
Integration with Register Columns
This library pairs well with wp-register-columns to display user field values in the admin users list:
// Register the field register_user_fields( [ 'department' => [ 'label' => __( 'Department', 'textdomain' ), 'type' => 'select', 'options' => [ 'sales' => 'Sales', 'support' => 'Support', 'dev' => 'Development', ], ], ] ); // Display in users list table register_user_columns( [ 'department' => [ 'label' => __( 'Department', 'textdomain' ), 'meta_key' => 'department', 'sortable' => true, 'display_callback' => function( $value, $user_id ) { return ucfirst( $value ) ?: '—'; }, ], ] );
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
GPL-2.0-or-later
Author
David Sherlock - ArrayPress