How to create a custom field in the user profile with PHP in WordPress

Contents

Introduction

This tutorial explains, in complete detail, how to create and manage a custom field in the WordPress user profile using PHP. It covers where to place code, important hooks, how to display the field in the admin profile screen and front-end registration forms, how to save and validate input securely, how to expose the field to the REST API, how to query users by the custom field, and other production concerns (security, sanitization, escaping, capability checks, multisite and caching considerations). Example code snippets are included all PHP examples are ready-to-drop into a plugin or themes functions.php (prefer creating a small plugin to avoid loss on theme updates).

Prerequisites and recommendations

  • WordPress version: any modern WP 5.x or 6.x. Functions used here are core and long-supported.
  • Where to put code: Prefer a plugin. If you put code in a theme, use a child theme to avoid losing changes when updating the parent theme.
  • Capabilities: Always check current_user_can(edit_user, user_id) when saving another user. For users editing their own profile, check appropriate capabilities.
  • Security: Always use nonces for forms, sanitize input with appropriate sanitizers (sanitize_text_field, sanitize_email, intval, esc_url_raw, wp_kses_post, etc.), and escape output (esc_attr, esc_html, esc_url) when rendering.
  • Storage: Use user meta (get_user_meta, update_user_meta) to store the custom field. Avoid adding columns to core tables.

Overview of hooks youll use

  • show_user_profile
  • edit_user_profile
  • personal_options_update
  • edit_user_profile_update
  • user_register
  • register_form
  • user_profile_update_errors
  • manage_users_columnsmanage_users_custom_column
  • register_rest_field
  • WP_User_Query->meta_query

Simple: Add a single text field to the WP user profile (admin screens)

The following example adds a simple Twitter Handle field to the profile page, saves it securely to user meta, and includes sanitization, capability checks and nonce protection. This is the canonical minimal pattern.

ID ) ) {
        return
    }

    // Use a nonce for verification
    wp_nonce_field( um_save_twitter_handle, um_twitter_handle_nonce )

    // Retrieve existing value
    twitter = get_user_meta( user->ID, twitter_handle, true )
    ?>
    

class=regular-text />

Notes on the example above

  • Use sanitize_text_field for simple text. For emails use sanitize_email. For URLs use esc_url_raw on save or sanitize_text_field if only a slug is allowed.
  • Use wp_nonce_field and wp_verify_nonce to avoid CSRF.
  • Use current_user_can(edit_user, user_id) to avoid unauthorized edits.
  • For localization, use __() or esc_html_e() as shown.

Validating input and showing errors

To validate and prevent saving invalid data on the admin profile screen, use user_profile_update_errors. The hook provides a WP_Error object you can add errors to.

add( twitter_handle_error, __( ERROR: The Twitter handle contains invalid characters or is too long., text-domain ) )
        }
    }
}
add_action( user_profile_update_errors, um_validate_twitter_handle, 10, 3 )
?>

Adding the field to registration forms (front-end)

If you want the field on the default registration form (wp-login.php?action=register), use register_form to display HTML and user_register to save the value after user creation. Note the default WP registration form posts to wp-login.php, so adding a nonce that WP will validate is tricky for custom registration forms, you control nonce handling better. Example below shows adding to the default registration form without a nonce — perform robust validation server-side.


    

Front-end custom registration form (preferred)

For better control add your own form (wp_ajax handlers or normal form posting to custom endpoint) and validate nonce with wp_nonce_field and check_admin_referer or wp_verify_nonce, then call wp_create_user() and update_user_meta().

Expose the custom field in the REST API

Add a custom REST field to the user resource so the field appears in user responses and can be updated via REST if desired. Provide get and update callbacks and a schema for proper serialization.

 function( user ) {
                return get_user_meta( user[id], twitter_handle, true )
            },
            update_callback => function( value, user, field_name ) {
                // Only allow if current user can edit the user in question
                if ( ! current_user_can( edit_user, user->ID ) ) {
                    return
                }
                sanitized = sanitize_text_field( value )
                if (  === sanitized ) {
                    delete_user_meta( user->ID, twitter_handle )
                } else {
                    update_user_meta( user->ID, twitter_handle, sanitized )
                }
            },
            schema => array(
                description => Twitter handle,
                type        => string,
                context     => array( view, edit ),
            ),
        )
    )
}
add_action( rest_api_init, um_register_rest_field_twitter )
?>

Show a custom field in the Users list table (admin)

To display the custom field as a column on the admin Users screen, use manage_users_columns and manage_users_custom_column filters and actions.

@ . esc_html( twitter ) . 
        } else {
            return 
        }
    }
    return value
}
add_action( manage_users_custom_column, um_users_custom_column, 10, 3 )
?>

Query users by custom field

To retrieve users matching a meta value, use WP_User_Query with a meta_query. Example: fetch all users whose twitter_handle meta equals bob.

 array(
        array(
            key     => twitter_handle,
            value   => bob,
            compare => =,
        ),
    ),
    fields => all_with_meta,
)
user_query = new WP_User_Query( args )
users = user_query->get_results()

foreach ( users as user ) {
    echo esc_html( user->display_name ) .  —  . esc_html( get_user_meta( user->ID, twitter_handle, true ) ) . n
}
?>

Other field types: checkbox, select, date, file

  • Checkbox: Save 1/0 or true/false using intval() when rendering use checked( 1, value, false ).
  • Select: Validate that the submitted value is one of allowed choices before saving.
  • Date: Sanitize and store as YYYY-MM-DD or timestamp using strtotime then intval. Use date_i18n to render in localized format.
  • File uploads: Prefer using a media uploader dialog (JavaScript) that returns an attachment ID. If allowing direct file uploads, validate file type and permission and use wp_handle_upload() and verify capabilities avoid arbitrary uploads from untrusted users.

Example: checkbox field

ID ) ) {
        return
    }
    wp_nonce_field( um_save_newsletter, um_newsletter_nonce )
    val = get_user_meta( user->ID, receive_newsletter, true )
    ?>
    

/>

Expose to and sanitize for front-end use

  • When rendering a user custom field in templates, always escape output: esc_html(), esc_attr(), esc_url() as appropriate.
  • Do not trust meta values for access control. Always verify capabilities with current_user_can before showing sensitive data or allowing modification.
  • If values are displayed in JavaScript, JSON-encode with wp_json_encode() or use esc_js() and localized data via wp_localize_script / wp_add_inline_script.

Internationalization and text domains

Wrap strings in __(), _e(), esc_html__(), etc. and replace text-domain with your plugins text domain. This makes the UI translatable.

Multisite considerations

  • User meta works across multisite. On multisite, user tables are shared be mindful of network-activated plugins and network administration screens.
  • Site-specific data belonging to a network user can still be stored as user meta. If you need site-specific settings per user per site, consider storing keys with site ID suffixes, e.g., pref_{blog_id}_something.
  • Super-admin capabilities: on multisite, check is_super_admin() if functionality should be limited.

Caching and object cache considerations

  • get_user_meta uses object cache changes via update_user_meta will invalidate the cache for that user. If you use persistent object cache, ensure it is functioning correctly.
  • When using WP_User_Query with meta_query, consider performance meta queries can be slow on large usermeta tables. Add appropriate indexes if you have many users and heavy querying, or maintain a separate lookup if necessary.

Alternative quick method: user_contactmethods filter

If you only need to add textual contact fields that appear in the Contact Info section, use the user_contactmethods filter. Limitations: you can only add text fields to Contact Info and not full markup sections or complex fields. Example:


Complete checklist before deploying to production

  1. Use nonces on forms or proper server-side verification for front-end forms.
  2. Sanitize all inputs on save using appropriate sanitizer functions.
  3. Escape all outputs using esc_attr / esc_html / esc_url / esc_js where applicable.
  4. Check capabilities (current_user_can) before updating or displaying sensitive data.
  5. Consider user roles and whether fields should be editable by certain roles only.
  6. Consider indexing usermeta if you will frequently run meta queries on a large user base.
  7. Add unit or integration tests if part of a larger plugin.
  8. Make strings translatable and provide a text domain for localization.
  9. Document the user meta key names and expected formats for other developers.

Troubleshooting common issues

  • Field not saving: Ensure your save callback is hooked to personal_options_update and edit_user_profile_update, check nonces, and ensure current_user_can passes.
  • Value not showing: Confirm get_user_meta uses the correct meta key, and that you echo with esc_attr or esc_html.
  • REST field not appearing: Ensure your register_rest_field runs on rest_api_init and the context includes view or edit for the place you are requesting the user object.
  • Performance issues with meta_query: Consider reducing queries, caching results, or adding database indexes meta_query scales poorly for large datasets.

Useful developer references

Conclusion

Adding a custom user profile field in WordPress is a common task. The recommended pattern is:

  1. Render HTML for the field on profile/edit screens with show_user_profile and edit_user_profile.
  2. Protect with a nonce and capability checks.
  3. Save sanitized values with update_user_meta in personal_options_update and edit_user_profile_update (and user_register for registrations).
  4. Expose via REST if needed with register_rest_field.
  5. Escape all outputs and validate inputs to avoid security issues.

Use the sample code above as a starting point and extend for more complex field types, validation or front-end UX (media uploader for files, JS-driven select widgets, etc.).



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Leave a Reply

Your email address will not be published. Required fields are marked *