How to add a WordPress login form to a page

In this article we will create a WordPress login form shortcode, display it on a page and allow users to login to wordpress using AJAX, in doing by capturing wordpress login requests and returing a JSON response.

Create a login form shortcode

Using the Shortcode API we register our new login_form shortcode using the add_shortcode function and rendering it using the built in function wp_login_form which outputs a copy of the core wordpress login form which is used on wp-login.php.

This means we do not have to build our own, and by default without implementing our AJAX solution the login form should just work, but it will redirect you to main wordpress login screen if you have trouble login in.

function jcul_login_form_shortcode($atts)
{
    return wp_login_form(['echo' => false]);
}

add_shortcode('login_form', 'jcul_login_form_shortcode');

Add the login form shortcode to a page

Using a shortcode in a WordPress page can be done in a multiple of ways for example if you are using the gutenberg editor you need to add a shortcode block to the page then enter the shortcode, if you are using the classic editor all you need to do is add the shortcode anywhere in the main content editor.

[login_form]

You are not restricted to outputting the new shortcode within a pages content, you can however output the wordpress login form using the do_shortcode function in your wordpress theme files.

do_shortcode('[login_form]')

Using AJAX to submit the WordPress login form

Converting the wordpress login form to an ajax login form allows users to attempt multiple times to login without leaving their current page, while displaying errors and successful login attempts. This is handy if you are trying to improve the user flow by avoiding the core wp-login.php file.

Our ajax wordpress login form implementation is quite simple we use jquery to capture the login form submission, prevent it redirecting to wp-login.php and instead send it an ajax request where depending on the JSON response we display a list of errors, or reload the current page once successful.

The button_text function is used to disable and change the text on the login form depending on the current state, This lets the user know something is happening and stops the user from submitting the form while it is waiting on a response.

(function($){

var button_text = function(el, text = 'Loading') {

  if (!el.data('text')) {
    el.data('text', el.val());
  }

  if (!text) {
    el.prop('disabled', false);
    el.val(el.data('text'));
  } else {
    el.prop('disabled', true);
    el.val(text);
  }
}

$('body').on('submit', '#loginform', function (e) {
  var $form = $(this);
  e.preventDefault();

  button_text($form.find('input[type="submit"]'));

  $.ajax({
    url: $form.attr('action'),
    type: 'POST',
    dataType: 'json',
    data: $form.serialize(),
    success: function (response) {
      $form.find('#user_pass').val('');

      if (response.status !== 'S') {
        $form
          .find('.jcul-ajax-error-message')
          .html('<p>An unknown error occured</p>');
        return;
      }

      if (response.data.logged_in !== 1) {
        $form
          .find('.jcul-ajax-error-message')
          .html('<p>' + response.data.errors.join('<br />') + '</p>');
        return;
      }

      location.reload();
    },
    complete: function () {
      button_text($form.find('input[type="submit"]'), false);
    },
  });
});

})(jQuery)

With the login form submission being captured we need to slightly modify the previously created login form shortcode by adding an container to display any returned messages, and a hidden field which sets a flag called jcul-ajax-enabled which we will use to check if the request has come from our ajax enabled wordpress login form.

function jcul_login_form_top($content)
{
    $content .= '<div class="jcul-ajax-error-message"></div>';
    return $content;
}

add_filter('login_form_top', 'jcul_login_form_top');

function jcul_login_form_bottom($content)
{
    $content .= '<input type="hidden" name="jcul-ajax-enabled" value="1" />';
    return $content;
}

add_filter('login_form_bottom', 'jcul_login_form_bottom');

Capture wordpress login form errors for AJAX response

Using the wp_login_errors filter we can capture all errors that occur when a user attempts to login, we first check to see if the jcul-ajax-enabled flag is set, if it is not then we eject out and return the list of errors and let wordpress continue doing what it does. If the flag is set then we output our json data response with logged_in = 0 to say we have failed to login and display list the wordpress errors.

function jcul_login_json_error_response($errors){

    if (!isset($_POST['jcul-ajax-enabled']) || !$errors->has_errors()) {
        return $errors;
    }

    echo wp_json_encode([
        'status' => 'S',
        'data' => [
            'logged_in' => 0,
            'errors' => $errors->get_error_messages()
        ]
    ]);
    exit;
}

add_filter('wp_login_errors', 'jcul_login_json_error_response', PHP_INT_MAX);

Capture successful AJAX login form response

The wp_login hook is triggered after a successful login attempt, similar to the capturing the login errors we use the jcul-ajax-enabled flag to confirm if we should output our json data response, setting logged_in = 1 to say it was a successful login.

function jcul_login_json_success_response(){

    if (!isset($_POST['jcul-ajax-enabled'])) {
        return;
    }

    echo wp_json_encode([
        'status' => 'S',
        'data' => [
            'logged_in' => 1
        ]
    ]);
    exit;
}

add_action('wp_login', 'jcul_login_json_success_response');

Conclusion

In this article we covered how to create a wordpress login form shortcode, display it on a page using gutenberg or classic editor, and convert the wordpress login form to submit login requests via AJAX and return a JSON response which updates the login form accordingly.

Leave a Reply

Fields marked with an * are required to post a comment