Custom User Profile Pic Upload on WordPress Profile Page | WordPress Development

profile pic on laptop screen with an upload icon on bluish background with a gear icon

We recently had a request from a client to develop functionality to allow custom user profile picture upload on the WordPress profile page. As it would eventually be used on the author section under every article on the site. We pointed out that they could easily add a Gravatar image which we could then use in the author byline.

They insisted they didn’t not want to use the Gravatar option and that there should be an option to upload the users image directly on the profile page itself.

We worked on the functionality and thought I would share the code here so it could help someone who is in need of something similar. And perhaps, someone can also help me better the code too. So it is going to be a win win for everyone 🙂

Let’s build the functionality together.

Summary of how we are doing to build it

We will be using the wp.media to handle and control the admin media modal. One of its primary uses is to create custom image upload links in the admin area. We will be integrating it into the Profile page of the Admin Dashboard.

Second, we will be writing some PHP code into the functions.php file using the following wp action hooks show_user_profile & edit_user_profile

The show_user_profile hook fires after the “About Yourself” table on the Profile page. And it only fires when the user is editing their own profile.

The edit_user_profile fires after the “About the User” settings table on the Edit User screen. This hook is mainly used to output fields at the bottom of the user’s profile page.

This PHP code would basically output the custom image upload fields at the bottom of the user’s profile page. It would let the user set a custom image, delete it, or set a new one whenever needed.

We will also include a jQuery file to manipulate the wp.media dialog and disable and enable buttons on the custom image section on the profile page.

PHP file code

I like to keep the functions.php file clean and lean. Therefore, we can create a new PHP file and name it something like custom-profile-img.php and place it in the /lib folder of the theme. You can, of course, name it anything you like, and place it in a similar folder like /inc to organize the files better. We will then link this file from the functions.php file like this:

require get_template_directory() . '/lib/custom-profile-img.php';

Now, the code that goes into the above PHP files is something like this:

//custom-profile-img.php

<?php
/**
 * Adds a custom profile image to the users profile page, apart from
 * the already available Gravatar option
 * 
 * @param $user
 */
function raydez_add_custom_profile_pic(  $user ) {
    $userid = $user->ID;

    //See if there is already a profile image saved as user meta
    $profile_img_id = get_user_meta( $userid, '_profile_img_id', true);

    //Get the image source. You get an array with the src as the first item.
    $profile_img_src = wp_get_attachment_image_src( $profile_img_id, 'thumbnail' );

    //check if the received array is valid
    $have_img = is_array( $profile_img_src );

    ?>
        <h2>Custom Profile Pic</h3>
        <table id="custom-img-table" class="form-table custom-img-table">
            <tr>
                <th><label for="custom-img">Custom Profile Image</label></th>
                <td>
                    <div class="custom-img-container">
                        <?php if( $have_img ) : ?>
                            <img src="<?php echo $profile_img_src[0] ?>" alt="" style="max-height:100px" />
                        <?php endif; ?>
                    </div>
                    <p class="hide-if-no-js">
                        <a class="upload-custom-img <?php if ( $have_img ) { echo 'hidden'; } ?>" href="#">Set Custom Image</a>
                        <a class="delete-custom-img <?php if ( !$have_img ) { echo 'hidden'; } ?>" href="#">Remove this image</a>
                    </p>
                    <input class="custom-img-id" name="custom-img-id" type="hidden" value="<?php echo esc_attr( $profile_img_id ); ?>" />
                </td>
            </tr>
        </table>
    <?php
}

add_action( 'show_user_profile', 'raydez_add_custom_profile_pic' );
add_action(  'edit_user_profile', 'raydez_add_custom_profile_pic' );


/**
* Enqueue the necessary JS files & functions
*
* @param int $pagenow
*/
function raydez_enqueue_admin_script( $hook ) {
    global $pagenow;
    $allowed_pages = array('profile.php', 'user-edit.php');

    if ( !in_array( $pagenow, $allowed_pages ) ) {
        return;
    }
    
    wp_enqueue_media();
    wp_register_script( 'custom-profile-image', get_template_directory_uri().'/script/profile-image.js', array('jquery-core'), false, true );
    wp_enqueue_script( 'custom-profile-image' );
}
add_action( 'admin_enqueue_scripts', 'raydez_enqueue_admin_script' );


/*
* Save custom user profile image id in user meta
*
*/
function raydez_save_custom_profile_img( $user_id ) {

    if ( !current_user_can( 'edit_user', $user_id ) ) {return false;}
        

    if(isset($_POST['custom-img-id'])) {
      $profile_img_id = sanitize_text_field( wp_unslash( $_POST['custom-img-id'] ) );
      update_user_meta( $user_id, '_profile_img_id', $profile_img_id );
    }
}

add_action( 'personal_options_update', 'raydez_save_custom_profile_img' );
add_action( 'edit_user_profile_update', 'raydez_save_custom_profile_img' );

Adding Custom Image Upload Section to the Profile page

The first function raydez_add_custom_profile_pic basically creates the fields for the custom profile pic upload section. Though the function is well commented and is self-explanatory, I will still try to summarize it for you.

First, we extract the current user’s id. Then we check if any custom image id is already saved to this user’s meta data. If we receive any saved image id, we then use it to get the actual image data array, which also includes the image URL as the first item in this array.

Then we simply check if the received array is a valid array, if yes, then we set the $have_img variable as true.

Then we simply display this image in an HTML image element.

Depending on whether we have an image or not, we either display the “Set Custom Image” link or the “Remove this image” link. We will also manipulate the CSS of these link buttons from the JS file too.

Enqueuing the admin scripts

The second function, raydez_enqueue_admin_script simply registers and enqueues the necessary admin files and functions needed for this to work.

Notice, we include the wp_enqueue_media function which enqueues all scripts, styles, settings, and templates necessary to use all media JS APIs. It is needed for the media upload dialog box to work.

Next, we register and enqueue our custom jQuery script, which I placed in the scripts folder, and named it profile-image.js

Also, I am checking to ensure that global $pagenow has a value of ‘profile.php‘ before enqueuing our custom JS file, so they only load on the user Profile page.

Saving the custom image to users meta data

The third function, raydez_save_custom_profile_img fires before the page loads on the ‘Edit User‘ screen. This function is attached to the following action hooks: personal_options_update & edit_user_profile_update

The first hook fires only when a user is viewing their own profile page. And the second hook fires when a user is viewing another user’s profile page.

We use these two hooks to save the custom field values on the WordPress Profile page.

We check if the super global variable $_POST[] contains our variable custom-img-id by checking: isset($_POST['custom-img-id']

If yes, we proceed with updating the user meta value for the key, _profile_img_id

JS File Code

We will be using jQuery to handle the media upload. Listing the code that goes in the JS file below:

//profile-image.js

jQuery(function($) {

    //Set all variables to be used in scope
    let frame;
    const outerWrap = $('#custom-img-table');
    const addImgLink = outerWrap.find('.upload-custom-img');
    const delImgLink = outerWrap.find('.delete-custom-img');
    const imgContaier = outerWrap.find('.custom-img-container');
    const imgIdInput = outerWrap.find('.custom-img-id');

    //Add Image Link
    addImgLink.on( 'click', e => {
        e.preventDefault();

        //If the media frame is already exists, reopen it.
        if (frame) {
            frame.open();
            return;
        }

        //Create a new media frame
        frame = wp.media({
            title: 'Select Your Profile Pic',
            button: {
                text: 'Use this media'
            },
            multiple: false     //Disable multiple file selection
        });

        //when an image is selected in the media frame
        frame.on( 'select', () => {

            //get media attachment details from the frame state
            const attachment = frame.state().get('selection').first().toJSON();

            //send attachment URL to our custom image input field.
            imgContaier.append( '<img src="'+attachment.url+'" alt="" style="max-height:100px;" />' );

            //send the attachment id to our hidden input
            imgIdInput.val( attachment.id );

            //hide the add image link
            addImgLink.addClass('hidden');

            //unhide the remove image link
            delImgLink.removeClass( 'hidden' );

        });

        //finally open the modal on click
        frame.open();
    });

    //Delete Image Link
    delImgLink.on( 'click', e => {
        e.preventDefault();

        //clear out the preview image
        imgContaier.html('');

        //unhide the add image link
        addImgLink.removeClass('hidden');

        //hide the delete image link
        delImgLink.addClass( 'hidden' );

        //delete the image id from the hidden input
        imgIdInput.val( '' );
    });

});

This is the profile-image.js file that we enqueued from the PHP file above. The JS file is also very well-commented and easy to follow. But I will still briefly explain it here.

First, we create all the variables we need for the function. These are basically references to our custom HTML elements on the Profile page.

We create references to the #custom-img-table element that wraps all our HTML elements on the Profile page. Then we create references to the Add Image link, the Remove Image link, the container for the custom image, and a hidden input that will hold the value of the image ID selected.

Then we add a click event listener to the addImgLink, which simply retrieves the image Id when an image is selected in the frame. And we populate the hidden input with this image id. So when the page is saved, we could retrieve this ID from the global $_POST[] variable and save it to the current user’s meta data.

Conclusion

So finally what we have created here is a custom image upload functionality for the user from the WordPress Profile page. The user can select any image from his computer or from the media gallery and save it to his profile. Basically the id of the chosen image is being saved to the user meta. And this image then can be used wherever needed.

For us the client didn’t want to use the Gravatar option to set his profile image, which would subsequently needed to be used to build the author byline section.

The following code should retrieve and display the specified users custom profile image:

<?php
  $profile_img_id = get_user_meta( $userid, '_profile_img_id', true);
  $profile_img_src = wp_get_attachment_image_src( $profile_img_id, 'thumbnail' );
  $have_img = is_array( $profile_img_src );
?>

<?php if( $have_img ) : ?>
  <img src="<?php echo $profile_img_src[0] ?>" alt="" style="max-height:100px" />
<?php endif; ?>

Make sure the $userid is passed the value of the user whose custom profile pic you want to display.

If you have any questions or need any clarifications, please feel free to ask me in the comments below.

About Amrit Ray

Amrit Ray is the founder of Raydez, a web design company based in India. He takes care of marketing and online business promotion & branding. He is also a singer/songwriter. He tries his best balancing both his passions giving adequate time to both. Follow him on LinkedIn | Twitter.