Creating a Drupal 6 custom login form – Step by Step tutorial.

Trying to find the right information on how to customise a Drupal 6 form has not proven to be easy. This article attempts to redress that balance.

[If you find any aspect of this post unclear or a little too hard to follow do please leave a comment below. If you’ve found this useful it’s always nice to say thanks.]

I decided to write this article just for you. If you’re trying to customise your own theme for a Drupal 6.x website then found that after you’ve got your basic CSS layout working and maybe a few nodes or pages displaying, you’ll probably be wanting to customise the following:

User login, user registration, forgotten password and the users account (home) page. I’ve found it a bit surprising just how little information there is available on the Interblob. What I’ve seen on the website is pretty vague or patchy at best and more often than not written for Drupal 5.x than for 6.x

You’re not alone. I’ve had exactly the same experiences as you and so after finally getting everything working myself I thought I’d write this post for others and try to make the method as clear as possible.

Remember if you break the front end to your site and find you can’t login, the way around that problem should be:

The only text you have to change above is the part, keep the rest of the text exactly as it is.

As we will be re-designing the whole login/signup/register process of our site it is very probable that things could get messy so it’s essential to know how to get yourself logged back in. In a worst case scenario and you find you can’t log yourself back in, you can repair your Drupal removed login by simply removing your customised login override function tpl.php file and then go to the URL above.

Also if you’ve lost your logout feature for what ever reason you can do so by typing:

(It’s pretty intuitive, but only easy when you know how…)

Almost every Drupal developer will either know of – or use – the Devel module. Just in case you don’t know about it, you can download it from here. It has a number of uses that make Drupal development easier but as with many things getting to know Drupal is an art –  It just takes a bit of support to help you get started off in the right direction…

If you have Devel activated in your Blocks and you have a separate Admin theme from your main site (highly recomended) then you should see this block in the admin part of your site.

Also if you’ve been tinkering with your *.tpl.php files already and find that sometimes changes show up and some times they don’t – apart from using the Devel Modules ‘Empty Cache‘ feature there is also a setting in the Devel Block > Devel Settings – At the very bottom of the list is a tick box ‘Rebuild the theme registry every page load‘.

Ticking this box will ensure that any changes you have made to your theme will be applied. This is fine whilst you’re developing the site but Drupal will remind you that when you go into production mode you really don’t want to leave this box ticked as it will take up far too much processor time serving each page.

And of coarse click Save configuration

1) You’ll need to copy and paste this code into the template.php file in your sites theme folder. If there isn’t one there, then – create one. So the location of your template.php folder should be… (Drupal will search for the existance of this file automatically).


function yourthemename_theme() {
 return array(
 'user_login' => array(
 'template' => 'user-login',
 'arguments' => array('form' => NULL),


function yourthemename_preprocess_user_login(&$variables) {
 $variables['intro_text'] = t('This is my awesome login form');
 $variables['rendered'] = drupal_render($variables['form']);

If you already have a function called yourthemename_theme() and you are in the process of wanting to add another customisation – say for example you’ve just customised the login screen and now you want to customise the registration page as well – this is what the function would look like:


function newagegiftshops_theme($existing, $type, $theme, $path)
 return array(

 'user_login' => array('template' => 'user-login','arguments' => array('form' => NULL)),
 'user_register' => array('template' => 'user-register','arguments' => array('form' => NULL))

Notice exactly how the code is layed out above. Using this method you can also add as many custom forms and pages as you like. As you can see the text in RED is the filename we need to use for our custom templates with the .tpl.php extension namely user-login.tpl.php also notice that the filename is user ‘dash‘ registration NOT user ‘underscore‘ as one might use in a Drupal 5 registration.


This is some example text to show it's coming from user-login.tpl.php
This prints the string $rendered, it has drupals login form in the string...
 <?php print $rendered; ?>

Now upload both these files (template.php and user-login.tpl.php) to your web server and then from your Admin pages of the Devel module you will need to Clear Cache, this is essential to have Drupal recognise the new user-login.tpl.php file.

If you now visit you will see that your new user-login template override file has kicked in.

The Username and Password form is being displayed because it is contained within the variable $rendered. If you remove the <?php print $rendered; ?> from your user-login.tpl.php file you will not see Drupal’s default login form which needs customising – which is what we’ll do next.

Also, I like the idea of being able to use your email address OR username to be able to login. Remembering multiple usernames can be a pain so being able to simply use your email address is much more user friendly.

You don’t need to use logintoboggan to do this. Logintoboggan is a very nice module but is unnecessary for our purposes. A module that will allow you to login simply by using either your username or email address is a much better option. It’s called the ’email registration’ module and you can download it from the Drupal site here:

3) Customising the login form.

With Drupal 6, all you have to do to make login’s work properly is to use the same form names and fields as Drupal core. To get all of these these all I did was to extract the field names from the HTML output by Drupal.

The raw HTML is as follows. All you have to do is to View Source and cut n’ paste the text between the <form> and </form> tags. Paste this text into your user-login.tpl.php file. Now you have the Drupal login code working but from your own page which you can now customise by wrapping this code in your own <div> tags or whatever CSS you wish to use – You can replace the entire file with this code below – Step 2 above was just a test so that you could see it working.


 <div id="edit-name-wrapper">
 <label for="edit-name">Username: <span title="This field is required.">*</span></label>
 <input type="text" value="" size="60" id="edit-name" name="name" maxlength="60">
 <div>Enter your 'My Site Name' username.</div>

 <div id="edit-pass-wrapper">
 <label for="edit-pass">Password: <span title="This field is required.">*</span></label>
 <input type="password" size="60" maxlength="128" id="edit-pass" name="pass">
 <div>Enter the password that accompanies your username.</div>

 <input type="hidden" value="<update><snip>id value removed for security</snip></update>" ... name="form_build_id">
 <input type="hidden" value="user_login" id="edit-user-login" name="form_id">
 <input type="submit" value="Log in" id="edit-submit" name="op">

Quite whether those ‘hidden’ fields need to have a value of “form-5be6…” I think probably not. All that’s left for you to do now is to create your own CSS fields to customise the look of your Drupal form login and away you go….

The more observant among you might notice that in the above code, there is no closing ?> That’s ok, leave it without one, Drupal core always add’s it’s own closing ?> anyway and on top of that our friends at Lullabot specifically recommend that you leave it out!

If after you’ve written your customisation code and you find that your template doesn’t ‘kick in’ it’s probably because the changes haven’t registered yet. Using the Devel Modules ‘Empty Cache’ is all well and good, but I’ve found that some times that doesn’t work either. In this case what you should do is this:

If this article has helped you in any way, please consider making a donation of £1.00 this helps pay for bandwidth and keeps this site up and running. Remember every little helps and your support is really appreciated. There is a ‘Donate’ link below.

Many thanks





  1. Comments  Drew   |  Sunday, 28 February 2010 at 8:00 pm

    Thanks. This is a great and simple tutorial! I had my own user page customized in less than 15 minutes. I have two clarifications and a question.


    1. Newbie PHP coders (like me) need to know that ” after the opening “<?php" line.

    My question is: How do I change the page title "User Account" and remove the three buttons, such as "Request a New Password"?

  2. Comments  Simon Nicol   |  Sunday, 28 February 2010 at 9:44 pm

    Hey Drew,

    According to they actually stipulate that it is better to leave off the closing ?> at the very end of a .tpl page because Drupal will add one automatically and in fact they recommend explicitly that you leave it out…

    I use Firebug a lot, if there are elements I want to get rid of, I just use firebug to locate either their CSS or their place in the final HTML and then search for what could be generating them in the PHP code. You will also find HTML on these pages – I then locate where the code is on the page and then either remove it from the PHP/HTML/CSS source.

  3. Comments  Drew   |  Sunday, 28 February 2010 at 8:06 pm

    (Oops – this forum module seemed to absorb my short code references above. Will try to restate again in less literal syntax…)


    1. Newbie PHP coders (like me) need to know that the opening ?php command does not need to be added in the template.php file since it is already there. They can just paste the function call, right?
    2. Your user-login.tpl.php code snippet gave me an error until I added ?> after the opening ?php line.

    My question is: How do I change the page title “User Account” and remove the three buttons, such as “Request a New Password”?

  4. Comments  Drew   |  Sunday, 28 February 2010 at 10:05 pm

    Thanks Simon.

    All I know is that using Drupal 6, I got an error when pasting the code above without the closing ?>. Once I added it, your code worked. So I’m happy. :-)

    By the way, in your second code block above I believe you’ve highlighted the wrong items in red. (e.g., user_login is in red, not user-login….)

    Thanks again for the great tutorial.

  5. Comments  Simon Nicol   |  Monday, 08 March 2010 at 8:36 pm

    Yes, you’re quite right Drew, I’ve just corrected it so it should look ok now for everyone else. Thank’s for pointing that out.


  6. Comments  llang   |  Tuesday, 09 March 2010 at 5:32 pm

    Simon: Many thanks for the write-up. It works like a charm and was very easy to follow.


    #1 – While it’s true that the Drupal coding standards say to leave off the closing PHP tags in your files, you don’t need any PHP tags at all except around actual PHP statements. If there’s no PHP in the file, the server should process it as normal HTML even though it has a PHP file extension. For example, the only PHP tags in my user-login.tpl.php file are around the print $rendered statement. Other than that, none.

    #2 – The best (read: easiest) way I’ve found to replace the title is with the String Overrides module. ( ) Works great although sometimes you have to do a search through the code to see what’s actually being passed to the t function.


  7. Comments  Simon Nicol   |  Tuesday, 09 March 2010 at 6:29 pm

    Thank’s for that Loren, I’m glad to hear it was useful.

  8. Comments  Joe   |  Saturday, 24 April 2010 at 4:03 am

    I dont get it. If my template.php has this in it, how will my code look?

    function magazeen_theme(&$existing, $type, $theme, $path){
    // Compute the conditional stylesheets.
    if (!module_exists(‘conditional_styles’)) {
    include_once ‘./’ . drupal_get_path(‘theme’, ‘magazeen’) . ‘/include/’;
    // _conditional_styles_theme() only needs to be run once.
    if ($theme == ‘magazeen’) {
    _conditional_styles_theme($existing, $type, $theme, $path);

  9. Comments  Simon Nicol   |  Sunday, 25 April 2010 at 5:11 pm


    It’s impossible to tell what it would look like Joe, you’re doing a conditional test and then including a CSS stylesheet. What I do is to grab the HTML output by Drupal, then I save that as my template-override. The next thing to do is to use a tool like Firebug (a plugin for Firefox) that will give you complete CSS control over the look of your site and then just ‘wrap’ the code that was output by Drupal in your new stylesheet.

  10. Comments  العاب   |  Sunday, 02 May 2010 at 12:19 pm

    Hi, i was trying to do an action for my site, for example if someone create a node with the type post, i want to send a email to my user. Only if the node is post type and not other type

  11. Comments  Matt   |  Saturday, 12 June 2010 at 4:46 am

    Hmmm…ok something is messed up lol.

    Its printing the user-login.tpl.php template within the page.tpl.php template.

    What did I do wrong? :)

  12. Comments  Ron Brash   |  Monday, 28 June 2010 at 10:03 pm

    Great article. Lets say now you wanted to remove the tabs on /user (the create user account, login, forgot password links)

    Would you have to do this via page-user-login.tpl.php and remove print $tabs? or could you write something in template.php? The first option isn’t all that appealing for me. Ideally it would be a simple function and the tabs would remain on /user/login etc…


  13. Comments  Stupidscript   |  Wednesday, 30 June 2010 at 10:28 pm

    Simon, It’s great to see tutorials/documentation being contributed by users who are figuring stuff out as they go. The biggest problem with Drupal is its documentation … or lack thereof. So, thanks for this.

    One note … not to disturb The Force or anything … but copying and pasting rendered code disables some of the security checks Drupal uses, not the least of which is your protection against remote form injections. Since you are using the same form-build-id for all of your registration submissions (and probably so are many of those who are reading your examples), you are inviting errors in user management and defeating Drupal’s validation and caching processes.

    The form-build-id is generated by Drupal’s form rendering functions using a (pseudo-)random string generator, and used both for caching purposes (so the form page does not need to be completely re-rendered with each request during the form processing … process) and as a check to prevent form submission forgeries (whereby each form gets a fresh form-build-id, and previously-used form-build-ids are detected as duplicates/forgeries).

    First the form-build-id is generated on-the-fly and stored in the database, and the form is displayed with that id in a hidden element which is passed via the POST method to each subsequent step in the process. When the form is submitted, the form-build-id is checked against the database entry for that form, before it is re-rendered for processing. If the form-build-id does not match the one in the database, it is probably because that form was submitted remotely, usually not for legitimate purposes, and it is considered to be an invalid form-build-id, and the form is refused.

    By using your method, you have a sweet-looking interface that utilizes none of the form-checking Drupal provides.

    Some of the issues you will face include duplicate usernames, duplicate email addresses and the potential for malicious users trying to mess with your system as described, above.

    (BTW, if you don’t think duplicate usernames or email addresses are an issue, ask yourself this: When someone requests a password reset … which username/email address gets the memo? Answer: The first one in the database, regardless of whether that is the exact user making the request. For example:

    uid = 1
    name = SomeUser’sExWife
    mail = (because she never got her own)

    uid =2
    name = SomeUser
    mail = (they used to be so happy together!)

    SomeUser requests a password reset … the memo goes to SomeUser’sExWife (who hates him) … she changes the password … SomeUser is forever locked out!)

    See: for the Drupal tutorials for using the Form API. The basic approach is to create a module with a menu reference to it, instead of copy-and-paste from a rendered page, and then using the module instead of the isolated page so the form can take advantage of Drupal’s form-checking processes. As usual, Drupal documentation is SEVERELY lacking, but hopefully you can work your way through it.

    Anyway, I’m a big fan of taking the initiative as you have done with posting this information, Simon. Thanks!

  14. Comments  Simon Nicol   |  Thursday, 01 July 2010 at 11:17 am

    Many thanks for the ‘heads up’. I’ve ammended the text to plug the hole. It certainly helps to find out these things, I’m just hacking away as I go and posting how I got through my trials and tribulations. I think a lot of my posts are for my own reference really but hope it helps others.

    Thanks again for the through comment – most helpful.

    All the best

  15. Comments  Mukesh   |  Sunday, 19 September 2010 at 4:45 am


    How to add the separate theme for Admin pages (That you have mentioned as one of the highly recommended things, and I also feel it is quite required)? I searched, but nothing concrete came up.


  16. Comments  Mukesh   |  Sunday, 19 September 2010 at 4:54 am

    Got the answer. Please ignore previous question.

  17. Comments  Locksmith Knoxville TN   |  Tuesday, 28 September 2010 at 2:21 am

    For whatever reason i’m ending up with a blank page once i make an attempt to post a comment,do you recognize how come its occurring?i’m implementing oprea web-browser

  18. Comments  Stupidscript   |  Tuesday, 28 September 2010 at 5:40 pm


    It is probably not just Opera. Check your server’s error logs. A completely blank page in PHP is frequently the result of a script error, like a missing curly brace or an extra dollar sign.

    The page displays, you complete and submit the comment form, you get a blank page … it’s an error in the form handling section of your code.

    Try to post a comment, then check your error logs. If you have PHP error_reporting turned on, either in php.ini, your server’s configuration file or in an .htaccess file, you should see a PHP error directing you to (a) what occurred and (b) which line of your code contains the error.

  19. Comments  gireesh   |  Saturday, 30 October 2010 at 6:18 pm

    This is a very helpfull document,Such a good explanation never i found any of the sites I searched for theming the login and register page.Also the importance of devel module you have wonderfully specified here.Really great.


  20. Comments  Adam   |  Friday, 05 November 2010 at 9:44 pm

    Thanks so much for your first few lines. i broke my front end and you fixed it in a jiffy )

  21. Comments  Bob   |  Thursday, 10 March 2011 at 9:03 am

    Simon thanks for posting this helpful tutorial, however I am a little confused.

    Right now I am working on my site locally using drupal 6. I am using the danland theme, and would like ONLY my login page to be pretty much blank, with a little bit of text, and a login form. Once the user has successfully logged in, I want them to be directed to my site pages (home, about us, photos, etc..) that have the danland theme applied to them.

    I kept the user-login file name as user-login.tpl.php. I put these two files in the danland theme at the path you specified. However when I follow your instructions, I still have the same user login page at the /user path with the danland them applied. The only thing changed was there was text “This is some example text to show it’s coming from user-login.tpl.php This prints the string $rendered, it has drupals login form in the string… ” being displayed. NOTHING has changed except this added text. What am I doing wrong?

    Thanks for your help!


  22. Comments  Simon Nicol   |  Thursday, 10 March 2011 at 2:07 pm

    It sounds as though you’re doing things correctly! The fact that you get “This is some example text…” shows that you have control of the log in form. After that I copied the login form output by Drupal in HTML format and then customized it with CSS to give the look and feel that I wanted…

  23. Comments  Bob   |  Thursday, 10 March 2011 at 6:07 pm

    Hm ok thanks..well I’m kind of new to CSS and HTML…I don’t want to customize the actual login fields (username, password). I like drupals. What I want is the actual page to be pretty much blank, only displaying maybe a header and some text. Right now the primary and secondary links (about us etc) are being displayed in this login form. I don’t want the users to see the content of my site until they have logged in and been authenticated. Any ideas?



  24. Comments  Felipe Luz   |  Tuesday, 12 July 2011 at 4:07 pm

    Thank you very much for your post. Very, very good.

  25. Comments  Tessa Alexander   |  Wednesday, 13 July 2011 at 6:21 pm

    Thanks this worked a treat!

  26. Comments  umesh   |  Monday, 01 August 2011 at 8:00 am

    It very good solution

  27. Comments  OLLI   |  Wednesday, 03 August 2011 at 8:09 pm

    Hello Simon,

    I’ve done all as you described but it doesn’t want to log in :( I don’t have any more ideas… have spent whole day and have got no solution… help me, please if you can

  28. Comments  Simon Nicol   |  Wednesday, 03 August 2011 at 9:41 pm

    Just a thought. The code I use won’t do for you because Drupal creates a ‘key’ (see one of the comments above) for each form. It’s the method described above that works. It’s a bit difficult to help you further without knowing exactly whats going on. :)

  29. Comments  Casey   |  Tuesday, 27 September 2011 at 4:04 pm

    You do not want to ignore that hidden field. It’s responsible for generating the build ID and while you may have used one in a more or less static approach, you need to understand that the build_id is a dynamically-generated value which is generated after each form generation. It’s used for validating uniqueness of forms!

  30. Comments  Simon Nicol   |  Tuesday, 27 September 2011 at 4:29 pm

    Do you know what PHP should go in it’s place? I haven’t seen the code for this anywhere on the Internet – it’s almost as though it’s a closely guarded secret!

  31. Comments  amit   |  Thursday, 10 November 2011 at 7:05 am


  32. Comments  Lynn   |  Saturday, 03 December 2011 at 4:31 pm

    Thanks so much for posting this. It’s been very helpful.

    In addition to the elements inside the form tags, I also want to get the action attribute inside the tag itself. drupal_render only gets whats inside. How do I get $form[‘action’] and pass it to the template file in order to redirect the user to a different page other than the default. BTW I am using, for various reasons, I cannot implement this in a module. It has to be done in the template.php file. TIA!

  33. Comments  aniket   |  Friday, 23 December 2011 at 7:37 am


    i followed the same procedure and copied the source code of login form in user-login.tpl for css modification. I have fbconnect module enabled.

    The problem i am getting is i am getiing extra form genrated with action and id but not extra form element.Also Fb related information is getting generated two times

  34. Comments  jeewendrakumar   |  Sunday, 19 February 2012 at 7:19 pm


    i need two login block in same region one block having user anem and password other block with company id ,userid and password plz help how can i do this.

Leave a Reply