Skip to content
wp-skills
Register
tools/wordpress-user-contact-methods-generator

WordPress User Contact Methods Generator

Add fields to the Contact Info section of the profile screen with the user_contactmethods filter. WordPress renders and saves them itself — optionally expose them in the REST API too.

Off adds to the list. On returns only yours, which also drops any another plugin added. WordPress itself ships none.

The profile screen needs none of this. Unregistered user meta is simply absent from /wp-json/wp/v2/users.

A commented get_the_author_meta() block showing where the escaping goes.

Contact methods1
  • #1

    The label beside the field on the profile screen.

    Lowercase letters, numbers and underscores. This is both the user meta key and the name of the input.

snippet.php
/**
 * Adds fields to the Profile screen's "Contact Info" section.
 *
 * The second parameter has a default because add_filter() is told to pass two
 * arguments: core always does, but a plugin calling
 * apply_filters( 'user_contactmethods', $methods ) with one would be an
 * ArgumentCountError on PHP 8 against a callback that requires both.
 */
function wp_skills_user_contact_methods( $methods, $user = null ) {
	$methods[ 'linkedin' ] = __( 'LinkedIn', 'text-domain' );

	return $methods;
}
add_filter( 'user_contactmethods', 'wp_skills_user_contact_methods', 10, 2 );

/*
 * No save routine, deliberately.
 *
 * WordPress saves these itself. edit_user() walks
 * wp_get_user_contact_methods() and assigns each posted value with
 * sanitize_text_field(), which is why a contact method needs no
 * personal_options_update hook, no nonce of its own, and no capability check —
 * the profile form's own checks already ran. Verified in Playground: with this
 * filter in place and nothing else, posting a field to edit_user() stored the
 * sanitized value, and a field left out of the request kept the value it had.
 *
 * Two consequences worth knowing. Core renders every contact method as a plain
 * text input, so these cannot be typed as url, email or date — the field type
 * is not yours to choose. And sanitize_text_field() strips tags but does not
 * validate, so a field meant to hold a URL can hold anything; escape it at the
 * point of output rather than trusting what is stored.
 */

// Reading the values in a template, inside the loop or with an explicit user id:
//
//   $linkedin = get_the_author_meta( 'linkedin', $user_id );
//   if ( $linkedin ) {
//       printf( '<a href="%s">%s</a>', esc_url( $linkedin ), esc_html( $linkedin ) );
//   }
//
// get_the_author_meta() returns the raw stored string, so it is escaped at the
// point of output: esc_url() for anything that becomes an href, esc_html()
// otherwise. A profile field is user input like any other.