A Web Design Community curated by Chris Coyier

Sending Nice HTML Email with PHP

By: Chris Coyier on 6/8/2009

This is going to be a continuation of the Website Change Request Form demo we’ve been using around here for a while. If you need to catch up, first I talked about it, then I built it, then I screencasted it, then I secured it. Throughout all of this, the end result has been a boring text-only email that gets sent to a single email address.

We’re going to improve that output, and make the email into a nicer looking HTML-formatted email.

It’s Not Much Different Than Text Email

Sending HTML Email through PHP uses the exact same mail function as text email:

mail($to, $subject, $message, $headers);

The last parameter, the headers, are optional for the function but required for sending HTML email, as this is where we are able to pass along the Content-Type declaration telling email clients to parse the email as HTML.

In fact, the headers area gives us the opportunity to do lots of important email functions. This is where we can set the From: and Reply To: settings if need be, as well as CC and BCC other recipients (Hey, a checkbox for CC’ing yourself would be a cool feature to add!). Here is the code used for the new and improved HTML-Sendin’ Website Change Request Form:

$to = 'bob@example.com';

$subject = 'Website Change Reqest';

$headers = "From: " . strip_tags($_POST['req-email']) . "\r\n";
$headers .= "Reply-To: ". strip_tags($_POST['req-email']) . "\r\n";
$headers .= "CC: susan@example.com\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";

Now We Can Use HTML Tags

The message parameter (a big string we pass to the mail function with the body of our email), can now have HTML tags in it. For example:

$message = '<html><body>';
$message .= '<h1>Hello, World!</h1>';
$message .= '</body></html>';

Here is whole shebang, to create the table of data as shown above:

$message = '<html><body>';
$message .= '<img src="http://css-tricks.com/examples/WebsiteChangeRequestForm/images/wcrf-header.png" alt="Website Change Request" />';
$message .= '<table rules="all" style="border-color: #666;" cellpadding="10">';
$message .= "<tr style='background: #eee;'><td><strong>Name:</strong> </td><td>" . strip_tags($_POST['req-name']) . "</td></tr>";
$message .= "<tr><td><strong>Email:</strong> </td><td>" . strip_tags($_POST['req-email']) . "</td></tr>";
$message .= "<tr><td><strong>Type of Change:</strong> </td><td>" . strip_tags($_POST['typeOfChange']) . "</td></tr>";
$message .= "<tr><td><strong>Urgency:</strong> </td><td>" . strip_tags($_POST['urgency']) . "</td></tr>";
$message .= "<tr><td><strong>URL To Change (main):</strong> </td><td>" . $_POST['URL-main'] . "</td></tr>";
$addURLS = $_POST['addURLS'];
if (($addURLS) != '') {
    $message .= "<tr><td><strong>URL To Change (additional):</strong> </td><td>" . strip_tags($addURLS) . "</td></tr>";
}
$curText = htmlentities($_POST['curText']);
if (($curText) != '') {
    $message .= "<tr><td><strong>CURRENT Content:</strong> </td><td>" . $curText . "</td></tr>";
}
$message .= "<tr><td><strong>NEW Content:</strong> </td><td>" . htmlentities($_POST['newText']) . "</td></tr>";
$message .= "</table>";
$message .= "</body></html>";

I think that looks A LOT nicer. And since this email is (theoretically) coming directly to you, isn’t it nice to know that it will be formatted to be easy on the eyes?

Concerns

Some people just absolutely hate HTML email. For one, it can be a security risk as it’s possible to run JavaScript in them in some email clients which can be problematic. HTML emails also have a habit of being more easily caught in Spam filters. I think it’s less of a concern here as this email is essentially being created BY you FOR you.

Demo

I updated the demo and download to use the new HTML email format.

View Demo   Download Files

There is likely to be another one or two demos on this form yet to come. I would at least like to do one on writing the data to a database before sending the email. Email can be such a fragile thing, that saving the data to a DB first is surely a smart move.

66 Responses

  1. Robert says:

    With HTML E-Mails I go the extra route of having a template file with the HTML and necessary variable markers. This way my HTML is separate from my PHP, and it makes it that much easier to make simple changes to the e-mails look.

    I cannot tell you how many times I’ve gone to edit HTML within a PHP variable or echo statement only to forget to add a slash before a double-quotation and then get an annoying error out of it all.

  2. Tim Wright says:

    Very good article, I was actually just asking someone on HTML e-mail tips last week. thanks!

  3. Marco says:

    Nice one Chris. Just one little addition: You really should look into the PHPMailer library: It’s better and easier than the “normal” mail() function.

    You can simply create a new instance:
    $var = new phpMailer();
    Set it to HTML
    $var->useHTML(true);
    Than add the body:
    $var->body(); // Mail stuff here
    And send it!
    $var->send();

    (This is just from the top of my head, the function names could be different)

    • I used to use a script similar to Chris’ but now I use zend’s validation class to validate the email and the mail class to send the email, it cares of everything for me, error handling etc. You can even specify a text only version, it’s pretty simple.

      This is how short it is.

      require_once ‘Zend/Mail.php’;
      $mail=new Zend_Mail();
      require_once ‘Zend/Validate/EmailAddress.php’;
      $validator=new Zend_Validate_EmailAddress();

      if($validator->isValid($_POST['email']))
      {
      // text only version, so strip the tags
      $mail->setBodyText(strip_tags($_POST['message']));
      // html version
      $mail->setBodyHtml($_POST['message']);

      $mail->setFrom($_POST['email'],’sender name’]);
      $mail->addTo(‘to@domain.com’,”receiver’s name”);
      $mail->setSubject(‘Subject goe heere’);
      // send email
      $mail->send();
      }
      else
      {
      foreach($validator->getMessages() as $errorMessage)
      {
      echo “$errorMessage”;
      }
      }

      The errors tell the user exactly what is wrong with the email if it’s not valid. You can strip tags you don’t want like javascript tags and/or links with the strip_tags function.

  4. alex says:

    Robert I like your idea but am not sure exactly how to implement, if you have an email template in html how do you include it in the php file and pass variables to it?

    Any links/guides that you could point me towards?

    • Robert says:

      The function I use simply loads a file, and then replaces variables in that file (in my case, variables are encased in brackets like {TITLE} or {BODY} ) with the specified results (ex. {TITLE} => $foo or {TITLE} => “My Homepage”.

      Since I tend to output as $variableName = callToMyFunction(“filename.tmpl”); $variableName already contains the entire bit of HTML… it’s that simple.

  5. Sean O says:

    Kudos for using “its” vs. “it’s” as the focus of your demonstration.
    It is the bane of so many otherwise-smart developers.

    http://tr.im/itsits

  6. Gareth Heyes says:

    Erm you get a lot of spam right?

    strip_tags is suppose to sanitize the From: address etc right? Wrong! Remember how SMTP works, \n\r can inject headers, you need to filter those. A single dot on a line can end the DATA transaction. You need to filter those.

    I suggest you read the notes on the manual page:-
    http://uk.php.net/manual/en/function.mail.php

  7. James says:

    Awesome article. I’m just about to make a contact form, so this will be very helpful indeed.

  8. Krzysiek says:

    I agree with Marco. People should consider using 3-rd party libs such as PHPMailer or Swift, rather then pure mail function from php, especially when you want to use SMTP Auth. But anyway good example for beginners.

  9. TG says:

    Could of used this like 3 days ago. :)

  10. Great article! I have a question though, how would you set it up to send both HTML email and plain-text in the same message. I know it can be done, just never figured it out.

  11. Prasanth says:

    God article. However, it is prone to email injection

    Having strip_tags here:
    $headers = “From: ” . strip_tags($_POST['req-email'])
    does not help much.
    It only strips html tags. not \r\n

    You have to filter req-email for \r\n

    • If you have a bit of replacement code for that area, I’d be grateful, and I’ll update the example and download.

      • Prasanth says:

        Like this:

        $req-email = $_POST['req-email'];

        $req-email = preg_replace(‘=((||0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i’, null, $req-email);

        $headers = “From: $req-email\r\n”;
        $headers .= “Reply-To: $req-email\r\n”;

      • preg_replace(‘/[^a-zA-Z0-9\._\-@]/’,”,$_POST['req-email'])

        Would replace any characters not allowed in an e-mail address. However, I’d still recommend doing a more comprehensive preg_match to determine if the structure is of a proper email format, too.

      • I couldn’t get either of these to work. I copied and pasted and converted the stupid wordpress curly quotes to straight quotes, but no dice. Maybe something got messed up in the posting of them. I don’t know enough about RegEx to debug myself, but if I learn I’ll attempt to get this integrated.

      • I actually just posted something similar to this, except I covered sending a message with both HTML and plain text versions.

        I also did email address validation to make sure nothing explodes. The regex there works.

        LINK

        Good article!

      • Excellent.

        For the record, I stole this an implemented it so the From: business should be all nice and sanitized and safe now.

  12. I concur with other comments and recommend phpMailer. MIME messages look nice but what if the recipient doesn’t have MIME, or has chosen to not to view MIME. I believe good practice is to use MIME & Text versions of an email, which phpMailer simplifies greatly.

    However, Microsoft back-tracked on HTML email and now uses Word as a rendering vehicle for email which is far more restrictive. If a large number of email recipients are corporate people you’d be wise to read Campaign Monitor’s musings on the subject.

    Personally, I’ve now dropped back to text-only emails as this was the original intention of the developers. The email is smaller, more likely to render properly and if I *really* need to impress somebody, I just link to a web version of the broadcast. So PHP’s mail function and a regex to replace templated variables works for me every time.

    • Mark Petereit says:

      Don’t forget context here. In his specific example, he’s generating an HTML email to himself, so there is no reason to code for all receiving platforms.

  13. Corey says:

    I’ve been doing something that I think works well to prevent your email client from marking emails from your site as spam.
    Simply set the “From” header to something like ‘no-reply@mysite.com’ and then set the “Reply-To” header to the user’s email like normal.
    Now you can just add ‘no-reply@mysite.com’ to your contacts and you’ll always get your emails, but can still click reply and it will be sent to the correct email.

  14. Haven’t even read this through yet but I know it’s going to be great. Thanks in advance Chris!

  15. Zoli says:

    I only used plain version so far, but its time for change ;)

  16. kobi says:

    I’m sorry, but I don’t really see what’s new here? HTML emails have been around for ever…
    Also, in your mail you have an image from your domain (“<img src=’http://css-tricks.com/ex…’”), you should note and warn people it will be blocked by most modern clients.

  17. 46Bit says:

    It’s rather worrying that people are writing tutorials and yet seem incapable of actually writing decent code. I had thought (or at least hoped) that all programmers with half a brain had a decent understanding of email headers by now… anyway, I suppose the actual tutorial will be quite useful for a lot of beginners – though showing them how to make spam vulnerable forms !== sensible.

  18. Farid Hadi says:

    Hey Chris,

    Nice article but I think you should also mention that you shouldn’t use divs and floats in email templates and instead use tables and inline styles. I know that’s what you have used but I’m just saying that you should mention to your readers why you are doing this.. which is because all our email clients are still stuck in the stone age :)

  19. Ben says:

    $message = '';
    $message .= '';
    $message .= '';
    $message .= "Name: " . strip_tags($_POST['req-name']) . "";
    $message .= "Email: " . strip_tags($_POST['req-email']) . "";
    $message .= "Type of Change: " . strip_tags($_POST['typeOfChange']) . "";
    $message .= "Urgency: " . strip_tags($_POST['urgency']) . "";
    $message .= "URL To Change (main): " . $_POST['URL-main'] . "";
    $addURLS = $_POST['addURLS'];
    if (($addURLS) != '') {
    $message .= "URL To Change (additional): " . strip_tags($addURLS) . "";
    }
    $curText = htmlentities($_POST['curText']);
    if (($curText) != '') {
    $message .= "CURRENT Content: " . $curText . "";
    }
    $message .= "NEW Content: " . htmlentities($_POST['newText']) . "";
    $message .= "";
    $message .= "";

    insted of doing this…. how about some heredoc:


    $name = strip_tags($_POST['req-name']) ;
    $email = strip_tags($_POST['req-email']);
    $typeChange = strip_tags($_POST['typeOfChange']);
    $urgency = strip_tags($_POST['urgency']);
    $urlMail = $_POST['URL-main'];
    $addURLS = ($_POST['addURLS'] != NULL) : "URL To Change (additional): " . strip_tags($_POST['addURLS']) . "";
    $curText = (htmlentities($_POST['curText']) == NULL) : "CURRENT Content: " . htmlentities($_POST['curText']) . "" ;
    $newText = htmlentities($_POST['newText']);
    $message = <<<EOD
    Name: $name";
    Email: $email
    Type of Change: $typeChange
    Urgency: $urgency
    URL To Change (main): $urlMail
    $addURLS
    $curText
    NEW Content: $newText";
    EOD;

    Save those bits and it is easier to read and use….

  20. Sankar says:

    Hey Chris,

    Nice Explanation. I have to test it out, Hope it works fine for me :)

    Thanks
    Sankar

  21. Jordan Walker says:

    To improve Web Accessibility you may consider adding the accesskey attribute to the label tag.

  22. I use phpmailer for this and works great. I suggest to send email in combined format (plain text/HTML).

    • Flyingrhinocmg says:

      How did you get it to work I couldn’t.

      I changed the contact information over and still nothing. Any ideas?

  23. Bogdan Pop says:

    I think there’s a problem with the URL field. I know what an URL is and how to format it, but how many of our clients know that? I think the field is to restrictive as it requires http and www to validate that field.

  24. Wow, I just had problems with HTML mails with PHP and then I got to this site. Nice work. You helped me out with some problems!

  25. Chris says:

    To those recommending PHPMailer, have a look at SwiftMailer. It provides the same sort of functionality but in (in my opinion) a much better designed set of classes.

  26. Óscar says:

    Leeeeeeeerrrrrrrrrroooooyyyyyyyyyyyy Jeeeeennnnnnnkkkkkiiiiiiiinnnnnssssss

    Nothing to do with the article, just had to say it!!!

  27. Good post, its a very simple way of formatting html emails!

  28. Here is a little trick I learned. If you don’t want to break your HTML up into a bunch of variables you can do this…

    $html = <<< newsletter

    $message…

    newsletter;

    mail($email, $subject, $html, $headers);

  29. zman says:

    Anyone have a PHP4 version? Mail is only sent successfully when running PHP5.

  30. Chris says:

    How can you add PHP content to the $body then ?

    I will get the title of the message from a form in a different page and send it via POST in the “send_newsletter” page.

    I will then need to have the Title i wrote in the Other page displayed on the E-mails which go out.

    How can that be done?

    Thanks

  31. TeMc says:

    Hi Chris,

    Excellent form you’re having here.
    I’ve been following it from the first version and this is quite a cool end result here.

    A small mistake I spotted during customization.

    in “index.php” on line 135 you forgot a period (” . “) at the second line of the $message… this is causing not to be part of the sent message.

    
                // PREPARE THE BODY OF THE MESSAGE
    
    			$message = '';
    			$message = '';
    			$message .= '';

    not that the second $message is also = instead of .=.

    For the rest… awesome form, best thing I’ve seen to date.


    TeMc

  32. Krinkle says:

    Hey CSS-Tricks,

    I’ve used this form for a freelancer’s website the other day and found myself customizing the mailoutput and thought I’d share the way I did it.

    In the html e-mail for all 1-line field (like names, addresses, etc.) this:

    $message .= strip_tags(stripslashes(trim($_POST['req-name'])));

    and for the text-area’s:

    $newText2 = strip_tags(stripslashes($_POST['newText']));
    $newText2 = nl2br($newText2);
    
    $message .= $newText2;
    

    stripslashes get’s right of annoying slash/’es where quotes are, which, depending on your server and framework settings are being put there automaticly.

    nl2br is handy to maintain linebreaks where entered in the form textarea’s.

    For non text-area’s the trim-function get’s rid of linebreaks and spaces at begin and end.


    My 2c,
    Krinkle

This comment thread is closed. If you have important information to share, you can always contact me.

* This website may or may not contain any actual CSS or Tricks.