Use the coupon code WORDPRESS and save 30% OFF! Buy Now

How to add custom fields to the WordPress registration form

Last Updated On
How to add custom fields to the WordPress registration form WordPress template

If you allow registrations on your WordPress site, chances are you need to gather some more information about your users. If you absolutely need this information, the best way to get it is to make it required during registration, otherwise there are slim chances that your users will actually visit their profile page and provide that optional information.

Sure, there are countless free and premium plugins that already do that, but you may want to do it manually, either because the plugins don’t do exactly what you want, or because you want to keep your website as lightweight as possible. Yes, there are tutorials also that tell you how to do it, but my research tells me that they are incomplete. What they all seem to be missing is new user registrations from the back-end (you know, when you -the administrator- add a user manually via the dashboard). Heck, the related WordPress codex page doesn’t even mention back-end registrations.

For this tutorial, we’ll add a required field that accepts the user’s year of birth and will be visible in the profile page. We won’t pay too much attention on validation, as it varies per use-case, but we will sanitize appropriately. Let’s see what it takes to add a new, required field in the default WordPress registration forms, from the start (what the user uses), to the middle (what the admin uses), to the end (what both see on the profile page).

The boilerplate

If your read our blog regularly, you’ve probably already guessed that we’ll create a new plugin to hold our code. If you’re not sure why, make sure you read our article regarding plugins vs child themes first. Create a new file in /wp-content/plugins/ named registration-fields.php and paste the following content:

Plugin Name: Custom Registration Fields
Plugin URI:
Version: 0.1
Author: CSSIgniter
Author URI:
License: GPLv2 or later
License URI:

Feel free to fill in the blanks or change any of the information. Save and activate the plugin.

Front-end registration

First of all, we’ll deal with the visitors’ facing registration form, as it’s usually the most used one. It requires that you’ve enabled user registration by going to Settings → General and checking the Anyone can register checkbox. If you’re working on a live website where really anyone can register, I strongly recommend adding a method to prevent spam registrations. Personally, I use the Invisible reCaptcha for WordPress plugin, and I’m very happy with it.

Screenshot of the default WordPress registration form.

The default WordPress registration form.

Displaying the field

Add the following in your plugin file:

* Front end registration

add_action( 'register_form', 'crf_registration_form' );
function crf_registration_form() {

$year = ! empty( $_POST['year_of_birth'] ) ? intval( $_POST['year_of_birth'] ) : '';

<label for="year_of_birth"><?php esc_html_e( 'Year of birth', 'crf' ) ?><br/>
<input type="number"
value="<?php echo esc_attr( $year ); ?>"

The action ‘register_form‘ fires right after the email field, so that’s where our new field will appear. Unfortunately there isn’t any other place we can hook. Line 8 checks the existence and sanitizes the value, in order to re-populate it in line 19. This is useful when the form re-appears, for example when a required value is missing or is wrong.

Right now we can visit the registration page and we’ll see that the field appears. You can find the link to your registration page, either by assigning the Meta widget (it includes a link to the registration form), or visiting directly http://your-website/wp-login.php?action=register

Screenshot of the default WordPress registration form with a Year of birth field

The default WordPress registration form with a Year of birth field.

Validating the field

If you now enter a username and email, and leave the year of birth is empty, no error will be thrown. Since we said the field will be required, let’s go ahead and fix that. Add the following:

add_filter( 'registration_errors', 'crf_registration_errors', 10, 3 );
function crf_registration_errors( $errors, $sanitized_user_login, $user_email ) {

if ( empty( $_POST['year_of_birth'] ) ) {
$errors->add( 'year_of_birth_error', __( '<strong>ERROR</strong>: Please enter your year of birth.', 'crf' ) );

if ( ! empty( $_POST['year_of_birth'] ) && intval( $_POST['year_of_birth'] ) < 1900 ) {
$errors->add( 'year_of_birth_error', __( '<strong>ERROR</strong>: You must be born after 1900.', 'crf' ) );

return $errors;

The ‘registration_errors‘ filter fires before the user is created, so we have a chance of adding our own error messages if something is missing, and avoid creating a user record with incomplete information. All we need is to $errors->add() our errors depending on our requirements. In this case, two different messages are used, depending if the year is missing or is less than 1900. This is were you’d validate a bit more, if for example you wanted to make sure a user is older than 18 years old, based on the year (or more other fields) he/she provided. Finally, we need to return $errors. While we don’t use $sanitized_user_login and $user_email, you could use them to throw more errors, if for example the user’s username contains your brand name.

Now, go ahead and fill in some of the registration form fields. Leave the year of birth empty, or enter a value less than 1900 and press Register. You should see a box listing all errors, including “Please enter your year of birth.” or “You must be born after 1900.“, the ones we added in the code inside crf_registration_errors().

Sanitizing and saving the field

If you do enter a valid year of birth at this point, a user will be registered but the year will not be saved. Add the following code to the plugin file:

add_action( 'user_register', 'crf_user_register' );
function crf_user_register( $user_id ) {
if ( ! empty( $_POST['year_of_birth'] ) ) {
update_user_meta( $user_id, 'year_of_birth', intval( $_POST['year_of_birth'] ) );

Action ‘user_register‘ fires after a user is inserted into the database. Only now can we store their year of birth, as we needed the user ID in order to work with user metadata. Make sure a value exists, and sanitize the value by passing it through intval() as it definitely needs to be an integer.

At this point, you may go ahead and fill in all the registration fields for a hypothetical new user. Once you press the Register button and no errors are thrown, their year of birth will have been stored in your database. How can you confirm that the value is actually stored? You can’t right now, not unless you want to dive into the actual database tables (it’s the wp_usermeta table, in case you’re interested). If you’re impatient to see that it works, jump to the Profile display section below, and then come back here to continue.

Back-end registration

It’s time to tackle the back-end registration, that is, the form that an administrator (or other role that can) registers users manually. This might be the only way to add new users if you don’t enable user registrations from Settings → General. You might consider this a bit redundant, but we shouldn’t leave any cases uncovered.

Screenshot of the default back-end WordPress registration form.

The default back-end WordPress registration form.

Displaying the field

The back-end registration form has a different format (and more fields) than the front-end form, so it requires different markup, not to mention that there are a whole different lot of actions and filters. To add display the year of birth field, add the following code to the plugin file:

* Back end registration

add_action( 'user_new_form', 'crf_admin_registration_form' );
function crf_admin_registration_form( $operation ) {
if ( 'add-new-user' !== $operation ) {
// $operation may also be 'add-existing-user'

$year = ! empty( $_POST['year_of_birth'] ) ? intval( $_POST['year_of_birth'] ) : '';

<h3><?php esc_html_e( 'Personal Information', 'crf' ); ?></h3>

<table class="form-table">
<th><label for="year_of_birth"><?php esc_html_e( 'Year of birth', 'crf' ); ?></label> <span class="description"><?php esc_html_e( '(required)', 'crf' ); ?></span></th>
<input type="number"
value="<?php echo esc_attr( $year ); ?>"

Just like the ‘register_form‘ action, the ‘user_new_form‘ action fires after all fields are displayed, so again, our field will be placed last before the Add New User button. Now, the ‘user_new_form‘ action is used twice in the WordPress core, once for new user registration, and once when assigning an user to a blog (in multisite installations), so an appropriate string is passed through $operation which we check and handle only when it’s equal to ‘add-new-user‘ (line 7).

The $year variable is set in exactly the same way as it was set in crf_registration_form() earlier. Same thing with the <input> field, except for the CSS class name (we re-use what WordPress provides). I’m repeating these as the wrapping elements are now different (following the WordPress format, we now display the field inside a table). We could of course refactor the input element so as not to repeat ourselves, but it would add some unnecessary complexity for the purposes of this tutorial. Finally, the heading on line 15 is not required, but is a nice touch nonetheless.

Go to your Dashboard → Users → Add New, and like previously, you can now see the field, however there’s no validation, no sanitization, and no saving happening at this point.

Validating the field

In order to validate our field, we’re going to use the ‘user_profile_update_errors‘ action.

add_action( 'user_profile_update_errors', 'crf_user_profile_update_errors', 10, 3 );
function crf_user_profile_update_errors( $errors, $update, $user ) {
if ( $update ) {

if ( empty( $_POST['year_of_birth'] ) ) {
$errors->add( 'year_of_birth_error', __( '<strong>ERROR</strong>: Please enter your year of birth.', 'crf' ) );

if ( ! empty( $_POST['year_of_birth'] ) && intval( $_POST['year_of_birth'] ) < 1900 ) {
$errors->add( 'year_of_birth_error', __( '<strong>ERROR</strong>: You must be born after 1900.', 'crf' ) );

Again, the validation bit (lines 7-12) is exactly the same as before, therefore it can be refactored, but let’s focus on the differences instead. The ‘user_profile_update_errors‘ action provides three parameters, $errors, $update and $user, that hold the errors, an update flag and a WP_User object respectively. In our case, $update contains a falsey value (and true if the code is reached by the profile update page), and the WP_User object has most of its properties blank, as there’s not actual user at this time. Since we don’t have any plans on allowing updates on the year of birth, lines 3-5 make sure we only proceed if $update is false.

Note that ‘user_profile_update_errors‘ is an action, in contrast with the earlier ‘registration_errors‘ which is a filter, and therefore we don’t need to return anything. In this case, $errors is passed by reference by the caller, so our $errors and the caller’s $errors is actually the same instance.

Now, if you want to test that validation works, you’ll to provide a username and an email, as those fields also have JavaScript validation and the form will not be submitted if they are empty. Alternatively, you can temporarily disable JavaScript on your browser.

Sanitizing and saving the field

While a valid year will pass validation and the user will be created, the year will not get saved at this point. We need this:

add_action( 'edit_user_created_user', 'crf_user_register' );

The ‘edit_user_created_user‘ action is our equivalent to the previously used ‘user_register‘ action. Both actions pass the $user_id as their first parameter. ‘edit_user_created_user‘ also passes a $notify string parameter (as in the last parameter of wp_new_user_notification()) but since we don’t do anything with it, we can re-use our crf_user_register() by hooking it without any modifications.

You guessed it. You can now register a new user, and their year of birth will persist.

Profile display

All this hard work, and we still can’t see a user’s year of birth. Let’s fix it!

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

function crf_show_extra_profile_fields( $user ) {
<h3><?php esc_html_e( 'Personal Information', 'crf' ); ?></h3>

<table class="form-table">
<th><label for="year_of_birth"><?php esc_html_e( 'Year of birth', 'crf' ); ?></label></th>
<td><?php echo esc_html( get_the_author_meta( 'year_of_birth', $user->ID ) ); ?></td>

The HTML markup inside crf_show_extra_profile_fields(), is again similar to crf_admin_registration_form() but instead of displaying an input field, we’re just printing the value.

Both the ‘show_user_profile‘ and ‘edit_user_profile‘ actions are exactly the same (pass the user as a parameter) and fire at the same point, but on slightly different cases. ‘show_user_profile‘ fires when you’re viewing your own profile page (or any user their page, for that matter), while ‘edit_user_profile‘ fires when you’re viewing another user’s profile (or any user that has the capabilities to do so). You could leverage this difference to, for example, create a scratchpad that only you can see, or a user rating feature where the user can’t vote for himself but see the ratings instead.

Enough with the examples. Make sure you create a user, and of course provide a year of birth. The system won’t let you proceed without it anyway! Then go to Dashboard → Users and Edit your new user. Scroll down, and voila!

Screenshot of the user profile screen showing the year of birth.

User profile screen showing the year of birth.


That was it! With just a few lines of code, we managed to add a required registration field, and learned a whole lot in the process. Some of the hooks can also be useful by themselves, allowing to further restrict registration criteria based on existing fields. Can an existing plugin do that? Perhaps. Can the ready-made plugin be as flexible and expressive as your code? Probably not!

What do you think? Did you find this tutorial useful? Do you have any questions? Leave a comment and let’s discuss!

The full code can be found in GitHub.

[box]Do you want to learn how you can make the fields editable through the User Profile page? Read the next tutorial: How to add a custom user field in WordPress.[/box]

62 responses to “How to add custom fields to the WordPress registration form”

  1. B. JayaKrishnan says:

    Is there way to display custom fields on top

    • Anastis Sourgoutsidis says:

      Unfortunately, no.
      As I mentioned in the post, the ‘register_form’ action is the only one included in the form, so we don’t really have any flexibility on positioning our fields.

      You can however build your own registration screen from scratch.

  2. D. Jansen says:

    Just what I need, nice and slick.
    However, the only thing I’m not able to do is edit the field in the backend, after the user is created.
    Of course date of birth is not something that can change overnight, but mistakes can be made, and it would be helpful for other fields. I’ve tried to implement this myself, but the only “new lines” that are being saved, are the lines in my php_error.log!

  3. D. Jansen says:

    And just as I left my, still pending, comment/question on updating in the backend, I succeeded myself. Just needed to use the “edit_user_profile_update” hook.

  4. Tutul says:

    Hey, thanks for the tutorial, this is exactly what i was looking for. I wanted to add phone field on registration page.
    Just one request can you tell me how to show these custom values in WP API – “/wp-json/wp/v2/users”.


    • Anastis Sourgoutsidis says:

      Unfortunately I haven’t worked with the REST API yet, so I can’t really help you.
      However it seems the register_rest_field() function is what you’re looking for. Just make sure it doesn’t exposes the phone numbers anywhere it shouldn’t.

  5. jose ramirez says:

    Nice article, not doing any project at the moment but my curiosity brought me here, I learned a lot about how to use WP hooks.

    Thank you!

  6. Chris Doyle says:

    Thank you, thank you! Even for someone not totally comfortable doing something like this, you have made it very easy to understand. Here’s where I am a little confused – and it may be because a plugin is not really working well (exactly what you’ve described).

    I always thought First Name and Last Name were part of WP core registration fields. If I edit a user I actually see those fields. When I use the registration plugin I don’t see them. When I asked that developer he said I would need to add them to the core.

    I ran through your example and bingo! Year of Birth is showing up in their registration form – that’s telling me First Name and Last Name must not be part of the core fieldset.

    Ugh! That said, do you have a tutorial or can you describe adding name fields as a requirement but not needing all the validation? Can I just leave the validation code out?

    Thanks again!

    • Anastis Sourgoutsidis says:

      You should always validate and sanitize your inputs.
      If you want to require something, then you definitely can’t skip validation. Checking if a field is empty and throwing an error if it is, therefore requiring it, is a simple form of validation.

  7. Mike says:

    Nice post, everything works fine. But if I want to edit this year of birth, what I could do?

  8. Vinicius says:

    Hey Anastis,
    I have a problem. in the localhost version the code above works perfectly, but when I try to update the version of my server online the meta_keys do not save, what causes the values ​​registered in the form to be lost

    • Anastis Sourgoutsidis says:

      Do you have different WordPress configuration? E.g. a plugin that is enabled on the server could conflict.

      If not, it could be ModSecurity. You’ll need to check your logs to confirm, or ask your host whether it’s enabled or not.

  9. Hi friend I left my, still pending, comment/question on updating in the backend, I succeeded myself. Just needed to use the “edit_user_profile

  10. Doug says:

    Thank you for creating this, I have found though that on the front end once a user enters their username and email it fails to perform validation for the rest of it, is there are a way I can fix this?

    • Anastis Sourgoutsidis says:

      Can’t possibly know why it fails, but the way to approach it is to disable any plugins that may interfere with the login page in any way (even better if you disable all of them) and then making sure that the crf_registration_errors() function (or whatever you have hooked on the ‘registration_errors’ filter) actually runs. You could do a die(‘message’) in it for example, just for testing.

      • Doug says:

        Oh curses, it’s due to an approve/deny new user plugin that is installed, I guess it is overriding the registration process. I need a way to have both custom fields on the admin screen and restrict a user from being able to see anything until approved by an admin. I will need to keep searching. Thanks for the suggestion to look into the plugins.

  11. Ikenna says:

    This is what i needed, but mine is to add a phone number field and be able to view it at the admin dashboard from the back-end, please can you help?. i tried editing the codes but was unsuccessful.
    i’ll be glad if you point me in the right direction. Thanks

  12. Ikenna says:

    Thanks Anastis;
    But i noticed this does not apply to a multi-site, as i succeeded with a single site and decide to try that on a multi-site; please can you point me right on how to archive same on a multi-site?

    • Anastis Sourgoutsidis says:

      I haven’t tried on multisite, but I don’t see a reason why it wouldn’t work.
      Can you share in a gist/pastebin the code you’ve tried, and explain what isn’t working exactly?

  13. Thanks a lot for this. Just few notes. When in a real plugin it’s worth to call these functions like

    // custom user fields
    add_action( ‘user_new_form’, array( $this,’crf_admin_registration_form’ ) );
    add_action( ‘user_profile_update_errors’, array( $this,’crf_user_profile_update_errors’), 10, 3 );
    add_action( ‘edit_user_created_user’, array( $this,’crf_user_register’) );
    add_action( ‘show_user_profile’, array( $this,’crf_show_extra_profile_fields’) );
    add_action( ‘edit_user_profile’, array( $this,’crf_show_extra_profile_fields’) );
    Specifically take care of the 2 parameters of add_action( ‘user_profile_update_errors’, array( $this,’crf_user_profile_update_errors’), 10, 3 );, since it’s a common error to write it down in the array…

    Another question. How to make the custom fields editable in the User Profile page? So far the crf_show_extra_profile_fields is only showing the fields, but I cannot edit them in place.

  14. Carlos says:

    hello! how can i add any other field not birth date with a custom validation? regards!

    • Anastis Sourgoutsidis says:

      You follow the tutorial, but you change the field name, the input type, the sanitization and the validation rules to match the field you want to add.

      • Yahya Aqrom says:

        Hi, i want to add custom fields such as full name, born date, home adress, line username, and instagram username. I’ve tried your suggestion to change the field name, the input type, the sanitization and the validation rules, but it cause a fatal error.. i think it is because i simply duplicate the code, change the field name, and paste in under the previous code. then i solved this problem by separating each code into multiple plugins..

        please tell me how to put the codes into just a single plugin..

  15. Dawid says:

    Your article is very usefull, thank you for that!

    I have a problem with errors for users. For registering, i’m using Woocommerce form.
    I want user to write his vat and company number while registering (required).

    This is that main code:

    I supose, that i have a problem over here:
    function crf_registration_errors( $errors, $sanitized_user_login, $user_email )

    Is that line compatible with woocommerce registering form?
    Now, user can leave that part (NIP and REGON) and register without it.

    I would like also to ask about that fields in admin panel. Not it show those numbers, but i can’t edit them. Is there any way to allow it?

    Thank you, Dawid

  16. Ahir Hemant says:


    have you any demo for upload image in default registration page(form) without plugin ?


    • Nik Vourvachis says:

      Hello Ahir. Uploading images in the default registration form is plugin functionality, a plugin is the optimal and proposed way to achieve this. Best regards.

    • Anastis Sourgoutsidis says:

      Hi Ahir,
      no we don’t.

      I would suggest against enabling image uploads on registration, as it could be misused (and you wouldn’t possibly know from whom, since they wouldn’t be registered).

  17. Yahia says:

    I need to do this but in my custom registration page , i have more fields to add but the problem in adding it to the database users table , i want to say i need to extend my table firstly and the users will use my registration page to be registered ( not the wordpress’s one )

  18. prashant says:


    This article was vary helpful for me i have made changes in this and it work good at update user form but when i create new user it not saving custom field value can you please guide me

    • Anastis Sourgoutsidis says:

      I can’t really help you if you don’t provide what errors you get, what your code is, etc.

      • prashant says:

        yes i can provide a code can i paste code here or make git repo

      • prashant says:


        i have used your code for register user field as below

        function crf_user_register( $user_id ) {
        if ( !isset( $_POST[‘force_reset_password’] ) ) {
        update_user_meta( $user_id, ‘force_reset_password’, intval( $_POST[‘force_reset_password’] ) );

        i don’t get any error on form submit but value not getting in database

        Thanks In advance

      • prashant says:


        thanks for your replay i found my mistake thank you for this great article once again

  19. prashant says:

    thank you for this very good article i used this and made my custom filed and it work fine with update user form but when i create new user the custom field is not insetting in database

  20. Andrew says:

    Thanks for inspiration, very good explanation. I made my repository for this type of plugin on github. Please come in and comment it on

    • ef says:

      I copied it but I get an error.

      Parse error: syntax error, unexpected ‘_e’ (T_STRING) in /homepages/42/d767018234/htdocs/clickandbuilds/Grupo23/wp-content/plugins/campos-formulario/formulario2.php on line 48

  21. Virg says:


    Thanks for this great article. I already followed some other tutorials but I couldn’t get my custom fields working.
    I still have one problem I can’t seem to fix myself though. One of my custom fields is just a numbers-field, and that’s working fine. But I also have a custom field where I need to submit a combination of letters and numbers. I changed the input type to “text”. As long as I fill in only numbers it’s working fine. But as soon as I fill in any letters, my input is saved to the database as “0”.
    I searched through the code but can’t seem to find the problem.

    • Nik Vourvachis says:

      Could you please paste your revised code in a Github gist or some other code sharing service so we can take a look? Most likely the input is still sanitized as a number instead of text, that’s why you get 0 instead of textual data.

    • Anastis Sourgoutsidis says:

      When sanitizing the field, you’ll need to change the sanitization function from intval() to sanitize_text_field().
      Also, if you added validation rules for the field, you’ll need to change the logic to correspond to what constitutes valid input for the specific field.

  22. Andrew says:

    What if I want the input field to be a string? And what if I want to compare that string against a fixed list of values and if none of them match, raise the error?

    For example:

    “Please enter the first scripture from the bible:”


    Gen 1:1
    Genisis 1:1



    Please provide the correct scripture to complete registration.

    • Anastis Sourgoutsidis says:

      You’d need to change the (or add an additional) input field from type=”number” to type=”text”, and modify the validation and sanitization code depending on your needs. For example, you’d probably sanitize the field with the sanitize_text_field() function, but you’ll need to build your validation code around the valid answer you’d expect.

      However it looks like you want to use this field as a form of spam protection? I’d say you’d better use a plugin built for this reason.

  23. This is super helpful! I’m wondering if there’s a way to access these custom fields within the wp_new_user_notification_email_admin? For example, if I wanted to have the email that goes to the admin upon new user registration say “A new user $name with the email $email has registered to your site $bloginfo. Their year of birth is $year.” Possible or no? Thanks!

    • Kelsey Barmettler says:

      Ok, I actually figured it out: I added the following to my plugin, using the variable $company_name rather than the above example of $year….

      add_filter( ‘wp_new_user_notification_email_admin’, ‘custom_wp_new_user_notification_email’, 10, 3 );

      function custom_wp_new_user_notification_email( $wp_new_user_notification_email, $user, $blogname ) {
      $wp_new_user_notification_email[‘subject’] = sprintf( ‘[%s] New user %s registered.’, $blogname, $user->user_login );
      $wp_new_user_notification_email[‘message’] = sprintf( “%s ( %s ) has registered to your site %s with the company name %s.”, $user->user_login, $user->user_email, $blogname, $user->company_name );
      return $wp_new_user_notification_email;

  24. Luis says:

    Hello. First let me tell you that this was one great tutorial. I created some custom fields with this and it almost work perfectly. My only issue is that I get the next error when registering :

    Fatal error: Uncaught Error: Call to a member function has_errors() on string in /website/wp-login.php:1345 Stack trace: #0 {main} thrown in /website/wp-login.php on line 1345

    I tried with my code and yours (the github one), but both get the same error. Any chance you know a fix?

  25. Luis says:

    O Ignore my comment. I had a form plug in that didn’t work active…

  26. Francisco Mosse says:

    Excellent! How do I create a input field that is a file? I need that users upload a CV.


    • Anastis Sourgoutsidis says:

      This would be much more involved, and needs careful consideration, as if done improperly it can open up your website to a whole lot of security vulnerabilities.

      I’d suggest you’d look for a plugin to do this for you.

      Otherwise, you’d start by looking at <input type="file"> and wp_handle_upload()

  27. Bappy says:

    Can you make a tutorial on file and image upload on front end registration or completely building a custom registration page from the start..

  28. Fachu says:

    Hi! Thanks for your contribution.

    I have a question: how can i edit field’s order?

    • Anastis Sourgoutsidis says:

      You can only change the order of the fields that you added yourself, by physically moving their code around.
      If there are other fields hooked on ‘register_form’ then you can change their position by un-hooking and re-hooking their functions with other priorities, or hooking your own accordingly.

Leave a Reply

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

Get access to all WordPress themes & plugins

24/7 Support Included. Join 115,000+ satisfied customers.

Pricing & Sign Up

30-day money-back guarantee. Not satisfied? Your money back, no questions asked.

Back to top