How to display user registration form on front-end of the website?

How to display the WordPress user registration form (the form that appears in “www.mywebsite.com/wp-register.php” page) in the front end of my blog?

I have customised the registration form. But don’t know how to call that form in the front end page. Any support will be really great help.

Read More

Thanks in advance. 🙂

Related posts

Leave a Reply

6 comments

  1. The process involves 2 steps:

    1. show the frontend form
    2. save the data on submission

    There are 3 different approach that comes to my mind to show the frontend:

    • use the built-in registration form, editing styles, etc to make it more “frontend like”
    • use a WordPress page/post, and display form using a shortcode
    • use a dedicate template not connected with any page/post, but called by a specific url

    For this answer I’ll use the latter. The reasons are:

    • using built-in registration form can be a good idea, deep customizations can be very hard using built-in form, and if one also want customize form fields the pain increase
    • use a WordPress page in combination with a shortcode, is not so reliable, and also I think that shorcodes should not be used for functionality, just for formatting and such

    1: Build the url

    All of us knows that default registration form of a WordPress site is often a target for spammers. Using a custom url is an help to solve this problem. In addition I want also use a variable url, i.e. the registration form url should not be always the same, this makes spammers life harder.
    The trick is done using a nonce in the url:

    /**
    * Generate dynamic registration url
    */
    function custom_registration_url() {
      $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
      return home_url( $nonce );
    }
    
    /**
    * Generate dynamic registration link
    */
    function custom_registration_link() {
      $format = '<a href="%s">%s</a>';
      printf(
        $format,
        custom_registration_url(), __( 'Register', 'custom_reg_form' )
      );
    }
    

    Using this functions is easy to display in templates a link to the registration form even if it’s dynamic.

    2: Recognize the url, first stub of Custom_RegCustom_Reg class

    Now we need to recognize the url. For the pourpose I’ll start to write a class, that will be finished later in the answer:

    <?php
    // don't save, just a stub
    namespace Custom_Reg;
    
    class Custom_Reg {
    
      function checkUrl() {
        $url_part = $this->getUrl();
        $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
        if ( ( $url_part === $nonce ) ) {
          // do nothing if registration is not allowed or user logged
          if ( is_user_logged_in() || ! get_option('users_can_register') ) {
            wp_safe_redirect( home_url() );
            exit();
          }
          return TRUE;
        }
      }
    
      protected function getUrl() {
        $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
        $relative = trim(str_replace($home_path, '', esc_url(add_query_arg(array()))), '/');
        $parts = explode( '/', $relative );
        if ( ! empty( $parts ) && ! isset( $parts[1] ) ) {
          return $parts[0];
        }
      }
    
    }
    

    The function look at the first part of the url after home_url(), and if it matches with our nonce it return TRUE. this function will be used to check our request and perform needed action to display our form.

    3: The Custom_RegForm class

    I’ll now write a class, that will be responsible to generate the form markup.
    I’ll use it also to store in a property the template file path that should be used to display the form.

    <?php 
    // file: Form.php
    namespace Custom_Reg;
    
    class Form {
    
      protected $fields;
    
      protected $verb = 'POST';
    
      protected $template;
    
      protected $form;
    
      public function __construct() {
        $this->fields = new ArrayIterator();
      }
    
      public function create() {
        do_action( 'custom_reg_form_create', $this );
        $form = $this->open();
        $it =  $this->getFields();
        $it->rewind();
        while( $it->valid() ) {
          $field = $it->current();
          if ( ! $field instanceof FieldInterface ) {
            throw new DomainException( "Invalid field" );
          }
          $form .= $field->create() . PHP_EOL;
          $it->next();
        }
        do_action( 'custom_reg_form_after_fields', $this );
        $form .= $this->close();
        $this->form = $form;
        add_action( 'custom_registration_form', array( $this, 'output' ), 0 );
      }
    
      public function output() {
        unset( $GLOBALS['wp_filters']['custom_registration_form'] );
        if ( ! empty( $this->form ) ) {
          echo $this->form;
        }
      }
    
      public function getTemplate() {
        return $this->template;
      }
    
      public function setTemplate( $template ) {
        if ( ! is_string( $template ) ) {
          throw new InvalidArgumentException( "Invalid template" );
        }
        $this->template = $template;
      }
    
      public function addField( FieldInterface $field ) {
        $hook = 'custom_reg_form_create';
        if ( did_action( $hook ) && current_filter() !== $hook ) {
          throw new BadMethodCallException( "Add fields before {$hook} is fired" );
        }
        $this->getFields()->append( $field );
      }
    
      public function getFields() {
        return $this->fields;
      }
    
      public function getVerb() {
        return $this->verb;
      }
    
      public function setVerb( $verb ) {
        if ( ! is_string( $verb) ) {
         throw new InvalidArgumentException( "Invalid verb" );
        }
        $verb = strtoupper($verb);
        if ( in_array($verb, array( 'GET', 'POST' ) ) ) $this->verb = $verb;
      }
    
      protected function open() {
        $out = sprintf( '<form id="custom_reg_form" method="%s">', $this->verb ) . PHP_EOL;
        $nonce = '<input type="hidden" name="_n" value="%s" />';
        $out .= sprintf( $nonce,  wp_create_nonce( 'custom_reg_form_nonce' ) ) . PHP_EOL;
        $identity = '<input type="hidden" name="custom_reg_form" value="%s" />';
        $out .= sprintf( $identity,  __CLASS__ ) . PHP_EOL;
        return $out;
      }
    
      protected function close() {
        $submit =  __('Register', 'custom_reg_form');
        $out = sprintf( '<input type="submit" value="%s" />', $submit );
        $out .= '</form>';
        return $out;
      }
    
    }
    

    The class generate form markup looping all the fields added calling create method on each of them.
    Each field must be instance of Custom_RegFieldInterface.
    An additional hidden field is added for nonce verifiction. Form method is ‘POST’ by default, but it can be setted to ‘GET’ using setVerb method.
    Once created the markup is saved inside the $form object property that is echoed by output() method, hooked into 'custom_registration_form' hook: in the form template, simply call do_action( 'custom_registration_form' ) will output the form.

    4: The default template

    As I said the template for form can be easily overriden, however we need a basic template as a fallback.
    I’ll wrote here a very rough template, more a proof of concept than a real template.

    <?php
    // file: default_form_template.php
    get_header();
    
    global $custom_reg_form_done, $custom_reg_form_error;
    
    if ( isset( $custom_reg_form_done ) && $custom_reg_form_done ) {
      echo '<p class="success">';
      _e(
        'Thank you, your registration was submitted, check your email.',
        'custom_reg_form'
      );
      echo '</p>';
    } else {
      if ( $custom_reg_form_error ) {
        echo '<p class="error">' . $custom_reg_form_error  . '</p>';
      }
      do_action( 'custom_registration_form' );
    }
    
    get_footer();
    

    5: The Custom_RegFieldInterface interface

    Every field should be an object that implements the following interface

    <?php 
    // file: FieldInterface.php
    namespace Custom_Reg;
    
    interface FieldInterface {
    
      /**
       * Return the field id, used to name the request value and for the 'name' param of
       * html input field
       */
      public function getId();
    
      /**
       * Return the filter constant that must be used with
       * filter_input so get the value from request
       */
      public function getFilter();
    
      /**
       * Return true if the used value passed as argument should be accepted, false if not
       */
      public function isValid( $value = NULL );
    
      /**
       * Return true if field is required, false if not
       */
      public function isRequired();
    
      /**
       * Return the field input markup. The 'name' param must be output 
       * according to getId()
       */
      public function create( $value = '');
    }
    

    I think that comments explain what classes implementing this interface should do.

    6: Adding some fields

    Now we need some fields. We can create a file called ‘fields.php’ where we define the fields classes:

    <?php
    // file: fields.php
    namespace Custom_Reg;
    
    abstract class BaseField implements FieldInterface {
    
      protected function getType() {
        return isset( $this->type ) ? $this->type : 'text';
      }
    
      protected function getClass() {
        $type = $this->getType();
        if ( ! empty($type) ) return "{$type}-field";
      }
    
      public function getFilter() {
        return FILTER_SANITIZE_STRING;
      }
    
      public function isRequired() {
        return isset( $this->required ) ? $this->required : FALSE;
      }
    
      public function isValid( $value = NULL ) {
        if ( $this->isRequired() ) {
          return $value != '';
        }
        return TRUE;
      }
    
      public function create( $value = '' ) {
        $label = '<p><label>' . $this->getLabel() . '</label>';
        $format = '<input type="%s" name="%s" value="%s" class="%s"%s /></p>';
        $required = $this->isRequired() ? ' required' : '';
        return $label . sprintf(
          $format,
          $this->getType(), $this->getId(), $value, $this->getClass(), $required
        );
      }
    
      abstract function getLabel();
    }
    
    
    class FullName extends BaseField {
    
      protected $required = TRUE;
    
      public function getID() {
        return 'fullname';
      }
    
      public function getLabel() {
        return __( 'Full Name', 'custom_reg_form' );
      }
    
    }
    
    class Login extends BaseField {
    
      protected $required = TRUE;
    
      public function getID() {
        return 'login';
      }
    
      public function getLabel() {
        return __( 'Username', 'custom_reg_form' );
      }
    }
    
    class Email extends BaseField {
    
      protected $type = 'email';
    
      public function getID() {
        return 'email';
      }
    
      public function getLabel() {
        return __( 'Email', 'custom_reg_form' );
      }
    
      public function isValid( $value = NULL ) {
        return ! empty( $value ) && filter_var( $value, FILTER_VALIDATE_EMAIL );
      }
    }
    
    class Country extends BaseField {
    
      protected $required = FALSE;
    
      public function getID() {
        return 'country';
      }
    
      public function getLabel() {
        return __( 'Country', 'custom_reg_form' );
      }
    }
    

    I’ve use a base class to define the default interface implemantation, however, one can add very customized fields directly implementing the interface or extending the base class and overriding some methods.

    At this point we have everything to display the form, now we need something to validate and save the fields.

    7: The Custom_RegSaver class

    <?php
    // file: Saver.php
    namespace Custom_Reg;
    
    class Saver {
    
      protected $fields;
    
      protected $user = array( 'user_login' => NULL, 'user_email' => NULL );
    
      protected $meta = array();
    
      protected $error;
    
      public function setFields( ArrayIterator $fields ) {
        $this->fields = $fields;
      }
    
      /**
      * validate all the fields
      */
      public function validate() {
        // if registration is not allowed return false
        if ( ! get_option('users_can_register') ) return FALSE;
        // if no fields are setted return FALSE
        if ( ! $this->getFields() instanceof ArrayIterator ) return FALSE;
        // first check nonce
        $nonce = $this->getValue( '_n' );
        if ( $nonce !== wp_create_nonce( 'custom_reg_form_nonce' ) ) return FALSE;
        // then check all fields
        $it =  $this->getFields();
        while( $it->valid() ) {
          $field = $it->current();
          $key = $field->getID();
          if ( ! $field instanceof FieldInterface ) {
            throw new DomainException( "Invalid field" );
          }
          $value = $this->getValue( $key, $field->getFilter() );
          if ( $field->isRequired() && empty($value) ) {
            $this->error = sprintf( __('%s is required', 'custom_reg_form' ), $key );
            return FALSE;
          }
          if ( ! $field->isValid( $value ) ) {
            $this->error = sprintf( __('%s is not valid', 'custom_reg_form' ), $key );
            return FALSE;
          }
          if ( in_array( "user_{$key}", array_keys($this->user) ) ) {
            $this->user["user_{$key}"] = $value;
          } else {
            $this->meta[$key] = $value;
          }
          $it->next();
        }
        return TRUE;
      }
    
      /**
      * Save the user using core register_new_user that handle username and email check
      * and also sending email to new user
      * in addition save all other custom data in user meta
      *
      * @see register_new_user()
      */
      public function save() {
        // if registration is not allowed return false
        if ( ! get_option('users_can_register') ) return FALSE;
        // check mandatory fields
        if ( ! isset($this->user['user_login']) || ! isset($this->user['user_email']) ) {
          return false;
        }
        $user = register_new_user( $this->user['user_login'], $this->user['user_email'] );
        if ( is_numeric($user) ) {
          if ( ! update_user_meta( $user, 'custom_data', $this->meta ) ) {
            wp_delete_user($user);
            return FALSE;
          }
          return TRUE;
        } elseif ( is_wp_error( $user ) ) {
          $this->error = $user->get_error_message();
        }
        return FALSE;
      }
    
      public function getValue( $var, $filter = FILTER_SANITIZE_STRING ) {
        if ( ! is_string($var) ) {
          throw new InvalidArgumentException( "Invalid value" );
        }
        $method = strtoupper( filter_input( INPUT_SERVER, 'REQUEST_METHOD' ) );
        $type = $method === 'GET' ? INPUT_GET : INPUT_POST;
        $val = filter_input( $type, $var, $filter );
        return $val;
      }
    
      public function getFields() {
        return $this->fields;
      }
    
      public function getErrorMessage() {
        return $this->error;
      }
    
    }
    

    That class, has 2 main methods, one (validate) that loop the fields, validate them and saving good data into an array, the second (save) save all data in database and send password via email to new user.

    8: Using defined classes: finishing the Custom_Reg class

    Now we can work again on Custom_Reg class, adding the methods that “glues” the object defined and make them works

    <?php 
    // file Custom_Reg.php
    namespace Custom_Reg;
    
    class Custom_Reg {
    
      protected $form;
    
      protected $saver;
    
      function __construct( Form $form, Saver $saver ) {
        $this->form = $form;
        $this->saver = $saver;
      }
    
      /**
       * Check if the url to recognize is the one for the registration form page
       */
      function checkUrl() {
        $url_part = $this->getUrl();
        $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
        if ( ( $url_part === $nonce ) ) {
          // do nothing if registration is not allowed or user logged
          if ( is_user_logged_in() || ! get_option('users_can_register') ) {
            wp_safe_redirect( home_url() );
            exit();
          }
          return TRUE;
        }
      }
    
      /**
       * Init the form, if submitted validate and save, if not just display it
       */
      function init() {
        if ( $this->checkUrl() !== TRUE ) return;
        do_action( 'custom_reg_form_init', $this->form );
        if ( $this->isSubmitted() ) {
          $this->save();
        }
        // don't need to create form if already saved
        if ( ! isset( $custom_reg_form_done ) || ! $custom_reg_form_done ) {
          $this->form->create();
        }
        load_template( $this->getTemplate() );
        exit();
      }
    
      protected function save() {
        global $custom_reg_form_error;
        $this->saver->setFields( $this->form->getFields() );
        if ( $this->saver->validate() === TRUE ) { // validate?
          if ( $this->saver->save() ) { // saved?
            global $custom_reg_form_done;
            $custom_reg_form_done = TRUE;
          } else { // saving error
            $err =  $this->saver->getErrorMessage(); 
            $custom_reg_form_error = $err ? : __( 'Error on save.', 'custom_reg_form' );
          }
        } else { // validation error
           $custom_reg_form_error = $this->saver->getErrorMessage();
        }
      }
    
      protected function isSubmitted() {
        $type = $this->form->getVerb() === 'GET' ? INPUT_GET : INPUT_POST;
        $sub = filter_input( $type, 'custom_reg_form', FILTER_SANITIZE_STRING );
        return ( ! empty( $sub ) && $sub === get_class( $this->form ) );
      }
    
      protected function getTemplate() {
        $base = $this->form->getTemplate() ? : FALSE;
        $template = FALSE;
        $default = dirname( __FILE__ ) . '/default_form_template.php';
        if ( ! empty( $base ) ) {
          $template = locate_template( $base );
        }
        return $template ? : $default;
      }
    
       protected function getUrl() {
        $home_path = trim( parse_url( home_url(), PHP_URL_PATH ), '/' );
        $relative = trim( str_replace( $home_path, '', add_query_arg( array() ) ), '/' );
        $parts = explode( '/', $relative );
        if ( ! empty( $parts ) && ! isset( $parts[1] ) ) {
          return $parts[0];
        }
      }
    
    }
    

    The constructor of the class accepts an instance of Form and one of Saver.

    init() method (using checkUrl()) look at the first part of the url after home_url(), and if it matches with the right nonce, it checks if the form was already submitted, if so using the Saver object, it validate and save the userdata, otherwise just print the form.

    init() method also fires the action hook 'custom_reg_form_init' passing the form instance as argument: this hook should be used to add fields, to setup the custom template and also to customize the form method.

    9: Putting things together

    Now we need to write the main plugin file, where we can

    • require all the files
    • load the textdomain
    • startup entire process using instantiating Custom_Reg class and call init() method on it using a reasonably early hook
    • use the ‘custom_reg_form_init’ to add the fields to form class

    So:

    <?php 
    /**
     * Plugin Name: Custom Registration Form
     * Description: Just a rough plugin example to answer a WPSE question
     * Plugin URI: https://wordpress.stackexchange.com/questions/10309/
     * Author: G. M.
     * Author URI: https://wordpress.stackexchange.com/users/35541/g-m
     *
     */
    
    if ( is_admin() ) return; // this plugin is all about frontend
    
    load_plugin_textdomain(
      'custom_reg_form',
      FALSE,
      plugin_dir_path( __FILE__ ) . 'langs'
    ); 
    
    require_once plugin_dir_path( __FILE__ ) . 'FieldInterface.php';
    require_once plugin_dir_path( __FILE__ ) . 'fields.php';
    require_once plugin_dir_path( __FILE__ ) . 'Form.php';
    require_once plugin_dir_path( __FILE__ ) . 'Saver.php';
    require_once plugin_dir_path( __FILE__ ) . 'CustomReg.php';
    
    /**
    * Generate dynamic registration url
    */
    function custom_registration_url() {
      $nonce = urlencode( wp_create_nonce( 'registration_url' ) );
      return home_url( $nonce );
    }
    
    /**
    * Generate dynamic registration link
    */
    function custom_registration_link() {
      $format = '<a href="%s">%s</a>';
      printf(
        $format,
        custom_registration_url(), __( 'Register', 'custom_reg_form' )
      );
    }
    
    /**
    * Setup, show and save the form
    */
    add_action( 'wp_loaded', function() {
      try {
        $form = new Custom_RegForm;
        $saver = new Custom_RegSaver;
        $custom_reg = new Custom_RegCustom_Reg( $form, $saver );
        $custom_reg->init();
      } catch ( Exception $e ) {
        if ( defined('WP_DEBUG') && WP_DEBUG ) {
          $msg = 'Exception on  ' . __FUNCTION__;
          $msg .= ', Type: ' . get_class( $e ) . ', Message: ';
          $msg .= $e->getMessage() ? : 'Unknown error';
          error_log( $msg );
        }
        wp_safe_redirect( home_url() );
      }
    }, 0 );
    
    /**
    * Add fields to form
    */
    add_action( 'custom_reg_form_init', function( $form ) {
      $classes = array(
        'Custom_RegFullName',
        'Custom_RegLogin',
        'Custom_RegEmail',
        'Custom_RegCountry'
      );
      foreach ( $classes as $class ) {
        $form->addField( new $class );
      }
    }, 1 );
    

    10: Missing tasks

    Now everithing is pretty done. We have just to customize the template, probably adding a custom template file in our theme.

    We can add specific styles and scripts only to the custom registration page in this way

    add_action( 'wp_enqueue_scripts', function() {
      // if not on custom registration form do nothing
      if ( did_action('custom_reg_form_init') ) {
        wp_enqueue_style( ... );
        wp_enqueue_script( ... );
      }
    });
    

    Using that method we can enqueue some js scripts to handle client side validation, e.g. this one. The markup needed to make that script work can be easily handled editing the Custom_RegBaseField class.

    If we want to customize the registration email, we can use standard method and having custom data saved on meta, we can make use of them in the email.

    Last task we probably we want to implement is prevent request to default registration form, as easy as:

    add_action( 'login_form_register', function() { exit(); } );
    

    All the files can be found in a Gist here.

  2. TLDR; Put the following form into your theme, the name and id attributes are important:

    <form action="<?php echo site_url('wp-login.php?action=register', 'login_post') ?>" method="post">
        <input type="text" name="user_login" value="Username" id="user_login" class="input" />
        <input type="text" name="user_email" value="E-Mail" id="user_email" class="input"  />
        <?php do_action('register_form'); ?>
        <input type="submit" value="Register" id="register" />
    </form>
    

    I found an excellent Tutsplus article on Making a fancy WordPress Register Form from scratch. This spends quite a lot of its time on styling the form, but has the following fairly simple section on the required wordpress code:

    Step 4. WordPress

    There is nothing fancy here; we only require two WordPress snippets,
    hidden within the wp-login.php file.

    The first snippet:

    <?php echo site_url('wp-login.php?action=register', 'login_post') ?>  
    

    And:

    <?php do_action('register_form'); ?>
    

    Edit: I’ve added the extra final bit from the article to explain where to put the above code snippets – its just a form so it can go in any page template or sidebar or make a shortcode out of it. The important section is the form which contains the above snippets and the important required fields.

    The final code should look like so:

    <div style="display:none"> <!-- Registration -->
            <div id="register-form">
            <div class="title">
                <h1>Register your Account</h1>
                <span>Sign Up with us and Enjoy!</span>
            </div>
                <form action="<?php echo site_url('wp-login.php?action=register', 'login_post') ?>" method="post">
                <input type="text" name="user_login" value="Username" id="user_login" class="input" />
                <input type="text" name="user_email" value="E-Mail" id="user_email" class="input"  />
                    <?php do_action('register_form'); ?>
                    <input type="submit" value="Register" id="register" />
                <hr />
                <p class="statement">A password will be e-mailed to you.</p>
    
    
                </form>
            </div>
    </div><!-- /Registration -->
    

    Please note that it’s really important, and necessary, to have user_login as a name and as an id attribute in your text input; the same is true for the email input. Otherwise, it won’t work.

    And with that, we’re done!

  3. I made a website some time ago that was displaying a customized registration form on the front end side. This website is not live anymore but here are some screenshots.
    login form
    registration form
    lost password form

    Here are the steps I have followed:

    1) Activate the possibility for all visitors to request a new account via Settings > General > Membership option. The registration page now appears at the URL /wp-login.php?action=register

    2) Customize the registration form so that it looks like your site front-end. This is more tricky and depends on the theme you are using.

    Here is an example with twentythirteen :

    // include theme scripts and styles on the login/registration page
    add_action('login_enqueue_scripts', 'twentythirteen_scripts_styles');
    
    // remove admin style on the login/registration page
    add_filter( 'style_loader_tag', 'user16975_remove_admin_css', 10, 2);
    function user16975_remove_admin_css($tag, $handle){
        if ( did_action('login_init')
        && ($handle == 'wp-admin' || $handle == 'buttons' || $handle == 'colors-fresh'))
            return "";
    
        else return $tag;
    }
    
    // display front-end header and footer on the login/registration page
    add_action('login_footer', 'user16975_integrate_login');
    function user16975_integrate_login(){
        ?><div id="page" class="hfeed site">
            <header id="masthead" class="site-header" role="banner">
                <a class="home-link" href="<?php echo esc_url( home_url( '/' ) ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
                    <h1 class="site-title"><?php bloginfo( 'name' ); ?></h1>
                    <h2 class="site-description"><?php bloginfo( 'description' ); ?></h2>
                </a>
    
                <div id="navbar" class="navbar">
                    <nav id="site-navigation" class="navigation main-navigation" role="navigation">
                        <h3 class="menu-toggle"><?php _e( 'Menu', 'twentythirteen' ); ?></h3>
                        <a class="screen-reader-text skip-link" href="#content" title="<?php esc_attr_e( 'Skip to content', 'twentythirteen' ); ?>"><?php _e( 'Skip to content', 'twentythirteen' ); ?></a>
                        <?php wp_nav_menu( array( 'theme_location' => 'primary', 'menu_class' => 'nav-menu' ) ); ?>
                        <?php get_search_form(); ?>
                    </nav><!-- #site-navigation -->
                </div><!-- #navbar -->
            </header><!-- #masthead -->
    
            <div id="main" class="site-main">
        <?php get_footer(); ?>
        <script>
            // move the login form into the page main content area
            jQuery('#main').append(jQuery('#login'));
        </script>
        <?php
    }
    

    Then modify the theme stylesheet to make the form appear as you want.

    3) You can further modify the form by tweaking the displayed messages :

    add_filter('login_message', 'user16975_login_message');
    function user16975_login_message($message){
        if(strpos($message, 'register') !== false){
            $message = 'custom register message';
        } else {
            $message = 'custom login message';
        }
        return $message;
    }
    
    add_action('login_form', 'user16975_login_message2');
    function user16975_login_message2(){
        echo 'another custom login message';
    }
    
    add_action('register_form', 'user16975_tweak_form');
    function user16975_tweak_form(){
        echo 'another custom register message';
    }
    

    4) If you need a front-end registration form, you will probably don’t want that registered users see the backend when they log-in.

    add_filter('user_has_cap', 'user16975_refine_role', 10, 3);
    function user16975_refine_role($allcaps, $cap, $args){
        global $pagenow;
    
        $user = wp_get_current_user();
        if($user->ID != 0 && $user->roles[0] == 'subscriber' && is_admin()){
            // deny access to WP backend
            $allcaps['read'] = false;
        }
    
        return $allcaps;
    }
    
    add_action('admin_page_access_denied', 'user16975_redirect_dashbord');
    function user16975_redirect_dashbord(){
        wp_redirect(home_url());
        die();
    }
    

    There are lots of steps, but the result is here !

  4. Way easier: use a WordPress function called wp_login_form() (Codex page here).

    You can make your own plugin so that you can use a shortcode in on of your pages:

    <?php
    /*
    Plugin Name: WP Login Form Shortcode
    Description: Use <code>[wp_login_form]</code> to show WordPress' login form.
    Version: 1.0
    Author: WP-Buddy
    Author URI: http://wp-buddy.com
    License: GPLv2 or later
    */
    
    add_action( 'init', 'wplfsc_add_shortcodes' );
    
    function wplfsc_add_shortcodes() {
        add_shortcode( 'wp_login_form', 'wplfsc_shortcode' );
    }
    
    function wplfsc_shortcode( $atts, $content, $name ) {
    
    $atts = shortcode_atts( array(
            'redirect'       => site_url( $_SERVER['REQUEST_URI'] ),
            'form_id'        => 'loginform',
            'label_username' => __( 'Username' ),
            'label_password' => __( 'Password' ),
            'label_remember' => __( 'Remember Me' ),
            'label_log_in'   => __( 'Log In' ),
            'id_username'    => 'user_login',
            'id_password'    => 'user_pass',
            'id_remember'    => 'rememberme',
            'id_submit'      => 'wp-submit',
            'remember'       => false,
            'value_username' => NULL,
            'value_remember' => false
    ), $atts, $name );
    
    // echo is always false
    $atts['echo'] = false;
    
    // make real boolean values
    $atts['remember']       = filter_var( $atts['remember'], FILTER_VALIDATE_BOOLEAN );
    $atts['value_remember'] = filter_var( $atts['value_remember'], FILTER_VALIDATE_BOOLEAN );
    
    return '<div class="cct-login-form">' . wp_login_form( $atts ) . '</div>';
    
    }
    

    All you have to do is to style your form on the frontend.

  5. If you’re open to the use of plugins, I’ve used the User Registration add-on for Gravity Forms before, it worked very well:

    http://www.gravityforms.com/add-ons/user-registration/

    Edit: I realise this isn’t a very detailed solution, but it does exactly what you need and is a good solution.

    Edit: To expand on my answer further, the User Registration add-on for gravity forms allows you to map any fields in a form created using Gravity Forms, to user-specific fields. For example, you can create a form with First Name, Last Name, Email, Website, Password. Upon submission, the add-on will map those inputs to the relevant user fields.

    Another great thing about it, is you can add any registered users into an approval queue. Their user accounts would only be created once approved in the backend by an admin.

    If the above link breaks, just Google “User Registration add on for Gravity Forms”