There are many tutorials and plugins for custom WordPress login and registration forms. In addition, the native WordPress login/register forms and functions do an excellent job. Even so, every site has its own needs which can require different ways of handling login and registration.
Here is one way to add Login and Register buttons to your site. This method is different from other tutorials that I found, specifically in these ways:
Key Features:
- No forms taking up space. Only buttons. Click a button to show the form.
- Log in or register from any page and stay on that page.
- No “Login” page, rather just buttons somewhere convenient on your site, such as the top right corner. Users can login/register from any page on your site and stay on that page after the login/registration is successful.
- When registering, they pick their own password. No emailed passwords, since I really don’t think that emailing passwords is safe.
- User is automatically logged in after registering.
- “Lost Password” form is filled out on front end, without redirecting to Admin back end.
- Logged-in users will instead see their name and a Logout button.
Here’s how it will look:
Try it yourself: Click the Log In button to open the login form. Click the “Lost Password” link on the login form to open the “Reset password” form. Click the Register button to show the Register form. Click “Hide” to hide the form.
How To Implement
You’ll need to add the functions, the JavaScript (note that this requires jQuery), and some CSS to your WordPress site. Then simply use the template tag in a template file where you want to show the buttons. This is broken into 4 easy steps. Step 1 lists all the functions that you must add to your functions. Step 2 shows you the necessary JavaScript. Step 3 shows you the necessary CSS. Step 4 show you how to use the template tag.
Step 1: Functions
You can simply copy all of the functions from Step 1 to your child theme’s functions.php
or to a custom plugin. These will work as is, but I’ll explain each a little bit in case you want to customize anything.
The 2 functions to process our registration form and log the user in after registration:
/** * Register And Login New User * * @param array $user_data * @return integer */ function isa_register_login_new_user( $user_data = array() ) { // Verify the array if ( empty( $user_data ) ) return -1; $user_args = array( 'user_login' => isset( $user_data['user_login'] ) ? $user_data['user_login'] : '', 'user_pass' => isset( $user_data['user_pass'] ) ? $user_data['user_pass'] : '', 'user_email' => isset( $user_data['user_email'] ) ? $user_data['user_email'] : '', 'user_registered' => date( 'Y-m-d H:i:s' ), 'role' => 'subscriber' ); // Insert new user $user_id = wp_insert_user( $user_args ); // Validate inserted user if ( is_wp_error( $user_id ) ) return -1; if ( $user_id < 1 ) return; // Login new user wp_set_current_user( $user_id, $user_data['user_login'] ); wp_set_auth_cookie( $user_id ); do_action( 'wp_login', $user_data['user_login'], get_userdata( $user_id ) ); return $user_id; } /** * Process Register Form * @param array $data Data sent from the register form */ function isa_process_register_form( $data ) { if( is_user_logged_in() ) { return; } if( empty( $_POST['isa_wp_register'] ) ) { return; } // Block spam bots if ( ! empty( $_POST['isa_hundred_acre_wood_field'] ) ) { return; } $reg_errors = array(); if( empty( $data['register_user_login'] ) ) { $reg_errors[1] = true; } if( username_exists( $data['register_user_login'] ) ) { $reg_errors[2] = $data['register_user_login']; } if( ! validate_username( $data['register_user_login'] ) ) { $reg_errors[3] = $data['register_user_login']; } if( email_exists( $data['register_user_email'] ) ) { $reg_errors[4] = $data['register_user_email']; } if( empty( $data['register_user_email'] ) || ! is_email( $data['register_user_email'] ) ) { $reg_errors[5] = true; } if( empty( $_POST['register_user_pass'] ) ) { $reg_errors[6] = true; } if( ( ! empty( $_POST['register_user_pass'] ) && empty( $_POST['register_user_pass_again'] ) ) || ( $_POST['register_user_pass'] !== $_POST['register_user_pass_again'] ) ) { $reg_errors[7] = true; } $referrer = $data['isa_register_permalink']; // Check for errors and register if none present if ( ! $reg_errors ) { isa_register_login_new_user( array( 'user_login' => $data['register_user_login'], 'user_pass' => $data['register_user_pass'], 'user_email' => $data['register_user_email'] ) ); $redirect = add_query_arg( array( 'isa_register' => 'successful' ), $referrer ); } else { $query_args = array(); $query_args['isa_register'] = 'failed'; // add query args for registration errors foreach ( $reg_errors as $k => $v ) { if ( $v ) { $error_key = 'e' . $k; $query_args[$error_key] = $v; } } $redirect = add_query_arg( $query_args, $referrer ); } wp_safe_redirect( esc_url_raw( $redirect ) ); } add_action( 'isa_user_register', 'isa_process_register_form' );
The second function above, isa_process_register_form
, processes the registration form. It checks for errors. If there are no errors, it calls the first function, isa_register_login_new_user
to register and log the user in. If errors were found, we add query args for the errors so that we can show error messages to the visitor. The user is safely redirected back to their current page.
Next, the function that fires our action when our registration form is submitted. This action, isa_user_register
, is used to hook our previous function to.
/** * Listen for our Registration form submit */ function isa_init() { if ( isset( $_POST['isa_register_action'] ) ) { do_action( 'isa_user_register', $_POST ); } } add_action('init', 'isa_init');
Next, the function to create a template tag that holds all the HTML for the buttons and forms. In this function, change all instances of ‘textdomain’ to your own textdomain.
/** * The template tag to display the login/register buttons. */ function isa_show_login_register() { // did they just try to register? $just_reg = empty( $_GET['isa_register'] ) ? '' : $_GET['isa_register']; // Get URL for redirecting back to current page if ( is_tax() ) { $permalink = get_term_link( get_query_var( 'term' ), get_query_var( 'taxonomy' ) ); } elseif ( is_post_type_archive() ) { $permalink = get_post_type_archive_link( get_query_var('post_type') ); } elseif ( is_home() ) { $permalink = get_permalink( get_option( 'page_for_posts' ) ); } else { $permalink = get_permalink(); } if ( is_user_logged_in() ) { // If they just registered, show a success message if ( 'successful' == $just_reg ) { ?> <span id="isa-register-success"><?php _e( 'Registration successful! Welcome to your journey!', 'textdomain' ); ?></span> <?php } else { // Show a button with a user icon and their name, linking to their profile page. // And a Log Out button $current_user = wp_get_current_user(); ?> <!-- Next is a link to the user's profile page. For example, if using bbPress, you can link to: bbp_get_user_profile_url( $current_user->ID ) If you don't have profile pages on your site, you can change this link instead to be a span element with a simple greeting. --> <a class="button isa-inline-buttons" href="#" title="<?php _e( 'View profile', 'textdomain' ); ?>"> <i class="fa fa-user"></i> <?php echo $current_user->display_name; ?> </a> <!-- Log Out button --> <a href="<?php echo wp_logout_url( get_permalink() ); ?>" class="button isa-inline-buttons" title="<?php _e( 'Log out', 'textdomain' ); ?>"><?php _e( 'Log Out', 'textdomain' ); ?></a><?php } } else { ?> <!-- Login and Register buttons --> <button type="button" id="isa_loginbutton"><?php _e( 'Log in', 'textdomain' ); ?></button> <button type="button" id="isa_registerbutton"><?php _e( 'Register', 'textdomain' ); ?></button> <?php // If login just failed, show login failed msg $login_failed = empty( $_GET['login'] ) ? '' : $_GET['login']; if ( 'failed' == $login_failed ) { ?> <span id="isa-login-error" class="box_error"><strong><?php _e( 'ERROR:', 'textdomain' ); ?></strong><br /><?php _e( 'Invalid username or password. Please try logging in again.', 'textdomain' ); ?></span> <?php } // If Lost Password form was just filled out, show Reset PW message $resetpw = empty( $_GET['resetpw'] ) ? '' : $_GET['resetpw']; if ( true == $resetpw ) { ?> <span id="isa-reset-pw"><?php _e( 'Check your email to reset your password.', 'textdomain' ); ?></span> <?php } // if Registration attempt just failed, show registration errors if ( 'failed' == $just_reg ) { $errors = ''; $e1 = empty( $_GET['e1'] ) ? '' : $_GET['e1']; $e2 = empty( $_GET['e2'] ) ? '' : $_GET['e2']; $e3 = empty( $_GET['e3'] ) ? '' : $_GET['e3']; $e4 = empty( $_GET['e4'] ) ? '' : $_GET['e4']; $e5 = empty( $_GET['e5'] ) ? '' : $_GET['e5']; $e6 = empty( $_GET['e6'] ) ? '' : $_GET['e6']; $e7 = empty( $_GET['e7'] ) ? '' : $_GET['e7']; if ( $e1 ) { $errors .= __( 'Invalid username.', 'textdomain' ) . '<br />'; } if ( $e2 ) { $errors .= sprintf( __( 'Username "%s" is already taken.', 'textdomain' ), $e2 ) . '<br />'; } if ( $e3 ) { $errors .= sprintf( __( 'Invalid username: %s.', 'textdomain' ), $e3 ) . '<br />'; } if ( $e4 ) { $errors .= sprintf( __( 'Email address "%s" already exists. Please log in.', 'textdomain' ), $e4 ) . '<br />'; } if ( $e5 ) { $errors .= __( 'Invalid email.', 'textdomain' ) . '<br />'; } if ( $e6 ) { $errors .= __( 'Please enter a password.', 'textdomain' ) . '<br />'; } if ( $e7 ) { $errors .= __( 'Passwords do not match.', 'textdomain' ) . '<br />'; } ?> <span id="isa-register-errors"> <strong><?php _e( 'Registration Error:', 'textdomain' ); ?></strong> <br /> <?php echo $errors; ?> </span> <?php } ?> <!-- Login Form --> <?php wp_login_form(array('form_id' => 'isa_loginform','value_remember' => true)); ?> <!-- Register Form --> <form name="isa_registerform" id="isa_registerform" action="" method="post"> <p class="register-username"> <label for="register_user_login"><?php _e( 'Username', 'textdomain' ); ?></label> <input type="text" name="register_user_login" id="register_user_login" class="input" value="" size="20" required /> </p> <p class="register-email"> <label for="register_user_email"><?php _e( 'Email', 'textdomain' ); ?></label> <input type="email" name="register_user_email" id="register_user_email" class="input" value="" size="20" required /> </p> <p class="register-password"> <label for="register_user_pass"><?php _e( 'Password', 'textdomain' ); ?></label> <input type="password" name="register_user_pass" id="register_user_pass" class="input" value="" size="20" required /> </p> <p class="register-password-again"> <label for="register_user_pass_again"><?php _e( 'Password Again', 'textdomain' ); ?></label> <input type="password" name="register_user_pass_again" id="register_user_pass_again" class="input" value="" size="20" required /> </p> <p id="isa-hundred-acre-wood"> <label for="isa-hundred-acre-wood-field" id="isa-hundred-acre-wood-label"><?php _e( 'For Official Use Only', 'textdomain' ); ?></label> <input name="isa_hundred_acre_wood_field" type="text" id="isa-hundred-acre-wood-field" value="" /> </p> <p class="register-submit"> <input type="hidden" name="isa_register_action" value="user_register" /> <input type="hidden" name="isa_register_permalink" value="<?php echo esc_url( $permalink ); ?>" /> <input type="submit" name="isa_wp_register" id="isa-wp-register" value="<?php _e( 'Register', 'textdomain' ); ?>" /> </p> </form> <!-- Lost Password form --> <form name="lostpasswordform" id="isa_lostpwform" action="<?php echo site_url('wp-login.php?action=lostpassword', 'login_post') ?>" method="post"> <p><strong><?php _e( 'Reset your password', 'textdomain' ); ?></strong> </p> <p> <label for="user_login"><?php _e( 'Username or E-mail:', 'textdomain' ); ?></label> <input type="text" name="user_login" id="user_login" value="" /> </p> <p class="submit"> <input type="hidden" name="redirect_to" value="<?php echo esc_url( $permalink ); ?>?resetpw=true"> <input type="submit" name="wp-submit" id="wp-submit" value="Get New Password" /> </p> </form> <?php } }
There are a couple spots where you may want to edit this last function.
- Line 14. Edit the success message shown to users after they register.
- Lines 35–37. This is a link with a Font Awesome “user” icon and the user’s name. I use this to link to the user’s profile page. Replace the “#” with the URL of your user’s profile page. If you don’t have a profile page for user’s, then change these 3 lines to a simple span element and something like “Hi, Name”, like so:
<span> <?php printf( 'Hi, %s', $current_user->display_name ); ?> </span>
I’ll explain the function above just a little.
If a user is logged in, this checks if the user was just registered. If the user just now registered, they see the “Registration Successful” message on line 14. If they did not just register, they see their name and a log out button.
If the visitor is not logged in, they see the “Log in” and “Register” buttons just like on the demo at the top of this page. If they just had a failed login attempt, they see a message saying, “Invalid username or password. Please try logging in again.” If they just submitted the “Forgot Password” form, they’ll see a message saying, “Check your email to reset your password.” If they just had a failed registration attempt, they’ll see the errors. For example, “Username ‘SomeName’ is already taken.”
Next, we have to add the “Lost Password?” link to the WP login form. We’re using the native WordPress login form, and calling it with wp_login_form. It does not, by default, include the “Lost Password” link. Add it like this:
/** * Add Lost Password? link to login form. */ function isa_lost_password_link() { return '<p><button id="lostpw">' . __( 'Lost Password?', 'textdomain' ) . '</button></p>'; } add_action( 'login_form_bottom', 'isa_lost_password_link' );
It’s important to keep the id="lostpw"
so that when clicked, it will show the form. Also, change ‘textdomain’ to your own textdomain.
If our visitor tries to log in with an incorrect username or password, this next function makes sure that he will stay on the same page and not be redirected to the back-end admin.
/** * Redirect back to front on failed login attempt. * Only catches incorrect username or password if both fields are filled in. * @todo doesn't work on empty fields. */ function isa_login_fail( $username ) { $referrer = $_SERVER['HTTP_REFERER']; // if there's a valid referrer, and it's not the default log-in screen if ( !empty($referrer) && ! strstr( $referrer,'wp-login' ) && ! strstr( $referrer,'wp-admin' ) ) { $url = add_query_arg( array( 'login' => 'failed' ), $referrer ); wp_safe_redirect( esc_url_raw( $url ) ); exit; } } add_action( 'wp_login_failed', 'isa_login_fail' );
The next function is not absolutely necessary, but it improves the user experience. Since we’re handling login and registration on the front end of the site, let’s remove the Lost Password and Register links from the back-end WP login page. There are 3 cases in which our visitors can end up back there. If we remove those back-end Password and Register links, the time spent back there will be short and sweet and our visitors will be redirected back to their original page on the front end. On the other hand, if they see these links and click on them, then it breaks our smooth redirect to the front, and our visitor will end up logged in to the back end (admin page). (The 3 cases in which our user can end up on the back-end WP login page are, 1. If they submit the login form with an empty field. Highly unlikely. 2. If they submit the “Forgot Password” form empty. Highly unlikely. 3. Clicking the Reset password link that they received through email.)
/** * Hide the Lost Password and Register links on wp-login. */ function isa_login_css() { echo '<style>body.login #login p#nav {display:none}</style>'; } add_action('login_head', 'isa_login_css');
That is the end of the functions.
Step 2: JavaScript (jQuery)
jQuery(document).ready(function (jQuery) { // detect click and/or touch if ('ontouchstart' in window) { evt = 'touchstart'; } else if (window.navigator.pointerEnabled) { evt = 'pointerdown'; } else if (window.navigator.msPointerEnabled) { evt = 'MSPointerDown'; } else { evt = 'click'; } // show login form jQuery('#isa_loginbutton').on(evt, function () { jQuery('#isa_loginform').slideToggle('fast', function () { // Hide Lost PW orm either way jQuery('#isa_lostpwform').hide(); if (jQuery('#isa_loginbutton').text() === 'Log in') { jQuery('#isa_loginbutton').text('Hide'); // hide register button jQuery('#isa_registerbutton').hide(); } else { jQuery('#isa_loginbutton').text('Log in'); // show register button jQuery('#isa_registerbutton').show(); } }); }); // show register form jQuery('#isa_registerbutton').on(evt, function () { jQuery('#isa_registerform').slideToggle('fast', function () { // Hide Lost PW form either way jQuery('#isa_lostpwform').hide(); if (jQuery('#isa_registerbutton').text() === 'Register') { jQuery('#isa_registerbutton').text('Hide'); // hide login button jQuery('#isa_loginbutton').hide(); } else { jQuery('#isa_registerbutton').text('Register'); // show login button jQuery('#isa_loginbutton').show(); } }); }); // show lost pw form jQuery('#lostpw').on(evt, function (e) { e.preventDefault(); // button should not submit anything // hide login form jQuery('#isa_loginform').hide(); // restrore button text jQuery('#isa_loginbutton').text('Log in'); // restore register button jQuery('#isa_registerbutton').show(); // show lost pw form jQuery('#isa_lostpwform').show(); }); });
Paste the JavaScript above into a new file. For example, name the file “login-register.js”. Then load it with wp_enqueue_script
. Be sure to add jQuery as a dependency in the 3rd parameter, like this:
wp_enqueue_script( 'my-login-register', get_template_directory_uri() . '/js/login-register.js', array( 'jquery' ) );
You must edit line 3 to be the URL of your new file, depending on where you place it. If you’re using a child theme, use get_stylesheet_directory_uri()
instead of get_template_directory_uri()
. If you’re placing this all into a plugin, use plugin_dir_url( __FILE__ )
instead of get_template_directory_uri()
, and note that plugin_dir_url( __FILE__ )
does add a trailing slash to the URL. So, you would remove the slash before ‘js/’ on line 3, like this:
plugin_dir_url( __FILE__ ) . 'js/login-register.js',
Step 3: CSS
You can add the CSS to any existing stylesheet, or else paste it into a new file and use wp_enqueue_style
to load it. (Or use the Custom CSS Jetpack Module, or any other Custom CSS plugin that lets you easily add custom CSS.)
Here are 3 blocks of CSS. The 1st is necessary to make the form work. The 2nd is optional CSS for visual styling. The 3rd is another block of optional CSS which will align the buttons to the right side in case you want to place the login/register buttons at the top right corner of your site.
This is necessary CSS for functionality and form structure:
/* Necessary CSS for functionality */ #isa_loginform, #isa_registerform, #isa_lostpwform { display:none; max-width:320px; } #isa-hundred-acre-wood, #isa-hundred-acre-wood-field, #isa-hundred-acre-wood-label { position: absolute; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); height: 1px; width: 1px; margin: -1px; border: 0px none; padding: 0px; } button#lostpw { border: none; outline:none; color: inherit; background-color: transparent; cursor:pointer; text-decoration:underline; padding-left: 0; text-transform: none; } #isa-reset-pw, #isa-register-errors, #isa-register-success, #isa-login-error { display: inline-block; padding: 10px; text-align: left; margin: 10px 0px; border: 1px solid; border-radius: 0.5em; } #isa-register-errors, #isa-login-error { color: #D8000C; background-color: #FFBABA; } #isa-reset-pw { color:#00529B; background-color:#BDE5F8; } #isa-register-success { color:#4F8A10; background-color:#DFF2BF; } #isa_lostpwform { margin-top:36px; }
This is optional CSS for styling. This makes the buttons look just like in the Demo at the top.
/* Optional CSS for styling */ #isa_loginbutton, #isa_registerbutton { border:0 } #isa_loginform input#wp-submit, input#isa-wp-register, #isa_lostpwform input#wp-submit, #isa_loginbutton, #isa_registerbutton { cursor: pointer; display: inline-block; padding: 10px 20px 11px; color: #FFF; font-family:"Trebuchet MS", Arial, Helvetica, sans-serif; font-size: 18px; line-height: 1.22em; text-transform: uppercase; letter-spacing: -1px; background-color:#004990; } #isa_loginform p, #isa_registerform p, #isa_lostpwform { text-align:left; } #isa_registerform label { min-width:136px; display:inline-block } #isa_loginform #user_login, #isa_loginform #user_pass, #isa_registerform #register_user_login, #isa_registerform #register_user_pass, #isa_registerform #register_user_pass_again, #isa_registerform #register_user_email, #user_login { min-height:36px; max-width: 100%; }
The following CSS is optional. It will align the buttons (and forms) to the right side in case you want to place the login/register buttons at the top right corner of your site.
/* Contain login/register buttons to right side */ #isa_loginbutton, #isa_registerbutton, #isa_loginform, #isa_registerform, #isa_lostpwform{ float:right; margin:0 2px 2px 2px; }
Step 4: Use the template tag where you want to display the login/register buttons
Place this template tag where you want to display the login and register buttons. For example, you can place it in the header.php
, near the top of your site, or in your sidebar.
<?php isa_show_login_register(); ?>
Questions and Comments are Welcome