The WordPress Theme Single Post, Post Attachment, & 404 Templates

You’ve built an index of all your posts, now you need to create a template to frame each piece of content (or missing content) on it’s own. The structure of single.php (and almost all the other templates we’ll be creating) is largely the same as index.php. In fact you can think of it as our template-template.

Update: We’ve created a second edition of this article, with updated code samples and coverage of the latest theme development techniques. Check it out at The WordPress Theme Single Post, Post Attachment, & 404 Templates. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

You’ve built an index of all your posts, now you need to create a template to frame each piece of content (or missing content) on it’s own.

The Template for Templates

The structure of single.php (and almost all the other templates we’ll be creating) is largely the same as index.php. In fact you can think of it as our template-template.

<?php get_header(); ?>

		<div id="container">
			<div id="content">

				<div id="nav-above" class="navigation">
				</div><!-- #nav-above -->

				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
				</div><!-- #post-<?php the_ID(); ?> -->

				<div id="nav-below" class="navigation">
				</div><!-- #nav-below -->

			</div><!-- #content -->
		</div><!-- #container -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

But there are going to be some notable differences. Starting with the_post() and comments_template().

We’ll be calling the_post() near the top of our page just after the opening of the content div and just before the navigation. We won’t need a loop in this template as WordPress knows just what post we’re looking at thanks to the_permalink().

And since this is a single post, we’ll need the comments_template(). And because we’ll be separating our comments and trackbacks when we come to coding up comments.php, we need to call it just like so:

<?php comments_template('', true); ?>

comments_template() will need to go just before the close of the #content div right after the navigation.

Single Post Navigation

Instead of using the poorly named next_posts_link() and previous_posts_link() we’ll be using the mostly accurately named previous_post_link() and next_post_link(). They do just what you think they do.

				<div id="nav-above" class="navigation">
					<div class="nav-previous"><?php previous_post_link( '%link', '<span class="meta-nav">&amp;laquo;</span> %title' ) ?></div>
					<div class="nav-next"><?php next_post_link( '%link', '%title <span class="meta-nav">&amp;raquo;</span>' ) ?></div>
				</div><!-- #nav-above -->
				<div id="nav-below" class="navigation">
					<div class="nav-previous"><?php previous_post_link( '%link', '<span class="meta-nav">&amp;laquo;</span> %title' ) ?></div>
					<div class="nav-next"><?php next_post_link( '%link', '%title <span class="meta-nav">&amp;raquo;</span>' ) ?></div>
				</div><!-- #nav-below -->

Single Post Titles

If you remember from our header.php lesson, we used a dynamic IF statement to clear the way for our Single post titles to take precedence on the page in the eyes of screen readers. We take advantage of this in this and all the rest of our Theme Templates by wrapping the title in and h1 tag.

					<h1 class="entry-title"><?php the_title(); ?></h1>

You’ll notice that our post title code is a little simpler too. The benefit of not having to link to anything now.

Single Post Entry Utility Links

The entry utility is … complicated. Here I think you’ll see the benefit of getting something right once and standing on the shoulders of others.

Before we take a look at the code we should think about why it is complicated. Because of the way comments work in WordPress we need to account for a few different scenarios: Open comments and trackbacks; only trackbacks open; only comments open; comments and trackbacks closed. And that means … what looks like a mess of IF statements.

It can be daunting. The code is commented but remember to look for the blocks of IF and ELSEIF statements and you’ll be fine.

We also want to print a link to our permalink here for bookmarking purposes and the RSS for this particular single post—useful for tracking developing conversations.

					<div class="entry-utility">
					<?php printf( __( 'This entry was posted in %1$s%2$s. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>. Follow any comments here with the <a href="%5$s" title="Comments RSS to %4$s" rel="alternate" type="application/rss+xml">RSS feed for this post</a>.', 'your-theme' ),
						get_the_category_list(', '),
						get_the_tag_list( __( ' and tagged ', 'your-theme' ), ', ', '' ),
						comments_rss() ) ?>

<?php if ( ('open' == $post->comment_status) &amp;&amp; ('open' == $post->ping_status) ) : // Comments and trackbacks open ?>
						<?php printf( __( '<a class="comment-link" href="#respond" title="Post a comment">Post a comment</a> or leave a trackback: <a class="trackback-link" href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'your-theme' ), get_trackback_url() ) ?>
<?php elseif ( !('open' == $post->comment_status) &amp;&amp; ('open' == $post->ping_status) ) : // Only trackbacks open ?>
						<?php printf( __( 'Comments are closed, but you can leave a trackback: <a class="trackback-link" href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'your-theme' ), get_trackback_url() ) ?>
<?php elseif ( ('open' == $post->comment_status) &amp;&amp; !('open' == $post->ping_status) ) : // Only comments open ?>
						<?php _e( 'Trackbacks are closed, but you can <a class="comment-link" href="#respond" title="Post a comment">post a comment</a>.', 'your-theme' ) ?>
<?php elseif ( !('open' == $post->comment_status) &amp;&amp; !('open' == $post->ping_status) ) : // Comments and trackbacks closed ?>
						<?php _e( 'Both comments and trackbacks are currently closed.', 'your-theme' ) ?>
<?php endif; ?>
<?php edit_post_link( __( 'Edit', 'your-theme' ), "nttttt<span class="edit-link">", "</span>" ) ?>
					</div><!-- .entry-utility -->

That wasn’t so bad was it?

Single Post Content

Unlike index.php, single.php content is pretty simple. Just one plain function call followed by wp_link_pages().

<?php the_content(); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&amp;after=</div>') ?>

Post Attachments

Not a lot of people use post attachments but they’re kinda interesting. When you add an image to your post you’re actually attaching it to the post. And, of course, you can attach more than just images. We’re going to make an attachment.php template but you can, if you like, adapt it further to cover other types of attachments like video, audio, and applications, by making video.php, audio.php, and application.php templates. There’s lots of different ways to be creative with attachment templates and WordPress.

The easiest way to proceed here is by copying single.php, renaming it attachment.php, and making the following changes.

First of all, delete the top navigation. We won’t need it at all here. Replace it with a page title that links back to your parent post.

				<h1 class="page-title"><a href="<?php echo get_permalink($post->post_parent) ?>" title="<?php printf( __( 'Return to %s', 'your-theme' ), wp_specialchars( get_the_title($post->post_parent), 1 ) ) ?>" rev="attachment"><span class="meta-nav">&amp;laquo; </span><?php echo get_the_title($post->post_parent) ?></a></h1>

Since the page title is now wrapped in h1 tags that means our post title should be wrapped in h2 tags.

					<h2 class="entry-title"><?php the_title(); ?></h2>

Now, because our attachment template needs to actually show the attachment, our content needs to reflect that. And since most attachments are going to be images, we’ll want to check for that and cover that scenario with an IF statement.

					<div class="entry-content">
						<div class="entry-attachment">
<?php if ( wp_attachment_is_image( $post->id ) ) : $att_image = wp_get_attachment_image_src( $post->id, "medium"); ?>
						<p class="attachment"><a href="<?php echo wp_get_attachment_url($post->id); ?>" title="<?php the_title(); ?>" rel="attachment"><img src="<?php echo $att_image[0];?>" width="<?php echo $att_image[1];?>" height="<?php echo $att_image[2];?>"  class="attachment-medium" alt="<?php $post->post_excerpt; ?>" /></a>
<?php else : ?>
						<a href="<?php echo wp_get_attachment_url($post->ID) ?>" title="<?php echo wp_specialchars( get_the_title($post->ID), 1 ) ?>" rel="attachment"><?php echo basename($post->guid) ?></a>
<?php endif; ?>
						<div class="entry-caption"><?php if ( !empty($post->post_excerpt) ) the_excerpt() ?></div>

<?php the_content( __( 'Continue reading <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )  ); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&amp;after=</div>') ?>

					</div><!-- .entry-content -->

Delete the bottom navigation from what was once your old single.php, and you’re done your attachment.php Template.

The 404 Template

A 404 Error is the server code for, “I can’t find this page” and it’s an event you need to take care of in your WordPress Themes. What happens when a link to your blog has a post url typed incorrectly? Or you unpublish a blog post? Your server coughs up a 404 error.

Luckily, WordPress has a template for that. It’s called, 404.php. The technique I stick with for 404 Templates is pretty straightforward but it works. Apologize and include a search form. There might be more creative solutions but none that get out of your visitor’s way faster.

Go back to your template-template above, drop the navigation and add something like this to the content.

				<div id="post-0" class="post error404 not-found">
					<h1 class="entry-title"><?php _e( 'Not Found', 'your-theme' ); ?></h1>
					<div class="entry-content">
						<p><?php _e( 'Apologies, but we were unable to find what you were looking for. Perhaps searching will help.', 'your-theme' ); ?></p>
	<?php get_search_form(); ?>
					</div><!-- .entry-content -->
				</div><!-- #post-0 -->

How To Create a WordPress Theme

This post is part of a WordPress Themes Tutorial that will show you how to create a powerful WordPress Theme from scratch. Read it from the beginning and code yourself up something awesome.

  1. WordPress Theme Tutorial Introduction
  2. Theme Development Tools
  3. Creating a Theme HTML Structure
  4. Template and Directory Structure
  5. The Header Template
  6. The Index Template
  7. The Single Post, Post Attachment, & 404 Templates
  8. The Comments Template
  9. The Search Template & The Page Template
  10. The Archive, Author, Category & Tags Template
  11. The Sidebar Template
  12. Reset-Rebuild Theme CSS & Define Your Layouts

71 thoughts on “The WordPress Theme Single Post, Post Attachment, & 404 Templates”

  1. Hi Ian,
    I´m stuck here. I’ve followed your instructions until the “Single post content” bit. I’ve copied the two simple lines of php code to after

    and before
    <!– #post- –>
    but the content doesn’t show up in the page. Eveything else is fine: title, utility, navigation, comments, but not the content.
    I’ve even tried with the same code from index.php, and with the_excerpt, but to no avail.
    Everything was fine when there was no single.php.
    What am I doing wrong here?

  2. I’ve found it. I was missing the_post() at the top. My bad…
    Thanks for the help

    1. I don’t get it –

      I had *exactly* the same problem you had. But isn’t everything (title, comments, etc) related to the_post()? Since we were both missing that line of code, how is it that we were able to get all the other data and just missed the_content() alone?

      1. Is not the same thing. To solve that problem you need to put

        just after the and it will work.

        Good Luck.

        Aaa, and my regards to Mr. Stewart for such a great tutorial.

    2. the_post() is not mentioned on this page of the tutorial. I missed it too. Maybe it should be mention on this page.

      Otherwise, this is a great tutorial.

  3. If previous_post_link() and next_post_link() really “do just what you think they do”, then they’ll keep changing their minds about what they do. It would be lovely if you could explain to us which means “older” and “newer”.

      1. I guess I was being overly slow, yes. Thanks, it makes sense now I’ve looked at the previous post. This is supposed to be a series, after all.

  4. A great tutorial so far, Ian, but could you provide the code in its entirety for single.php et al? You lost me when you addressed Single Post Titles – I am not sure if I am inserting the code snippet into header.php – as well as the sequential items.

  5. Hi Ian,
    I’ve solved my newb problem but I found another one, and this time, I think, is a bit more complicated because it’s not a problem of your theme, but of WordPress.
    Let me explain: If we use a picture in a post with an attachment page, the picture gets a title associated with that post. But if we re-use that picture in another post, the title in the second attachment page links to the original post, and not to this second one.
    I hope I was clear with my explanation:
    use photo in first post => attachment page OK
    re-use photo in second post => attachment page links to first post instead of second
    Any idea about this?

  6. This one seems a lot less clear about where everything goes than in the previous tutorials. There’s a lot of information, but I’m not sure where to put it all, since you don’t really say for a number of parts.

      1. I have to agree with Christina. I am trying to piece together my template following the narrative, not by jumping between the article, my template and the other file from the SVN repository.

        For example, the instruction about entry-utility is written before the_content() but they appear in the opposite order in the template. We might probably know the structure as it’s the same in index.php but many of us are beginners.

        Organizational clarity aside, I do appreciate the explanation behind the snippets of code; I am getting a lot out of it nonetheless. Thanks, Ian.

  7. I’m a little confused about attachment.php. Where does it come in? If I were browsing a blog, when would I ever encounter this page?

  8. For attachment.php, you said to remove the bottom navigation. However, I notice that the entry utility tags are still in place. Is it assumed that these are to be removed too, or should I leave them?

  9. in an earlier part of this 12 step tutorial, you introduced us to step #4. Template and Directory Structure. In that step you never mentioned needing either 404 or “single.php”. Did you just introduce them abruptly in this step, or did i miss an earlier introduction/mention to these files?


      1. thanks for the quick reply, ian. i love the early steps of this 12-step tutorial: for example, the concise explanations of elements such as doc type. but i gotta tell you, the middle-to-end steps become daunting for me. i tried to plow through steps 7-12, thinking i’d find some comfort in step 12 in particular, but the whole thing became overwhelming with it’s php intricacies. i’d rather it be 24 steps that are more simple, perhaps. that said, excellent work overall. your voice, your design – all flow very well together.

      2. Ian,

        I’m totally with Sergi on this one. Your tutorial series is superior to the only book out there (that I know of) on the subject, and by far the best resource I’ve yet to find on the web (and I’ve done nothing but looked for the last 2-3 weeks or so!). However, there’s a marked difference inn how much you develop topics from roughly mid-way into the segments and to the end. The gaps grow. There’s only so much one can leave for the reader to ‘figure out’ on there own.

        I truly believe you’ve demonstrated to have the talent and expertise to be the ‘Zen’ of WordPress Theme Tutorials, or if you will, the all classic ‘A-List-Apart’.

        If you have the interest and the time to invest in re-working the segments that lack adequate explanation, or better yet, expanding on the tutorial as a whole, you’d be doing this community an even greater service then you already have.

        In fact, I was just thinking that you might want to consider a screencast as that might be less tedious. Either way, your teaching style is unrivaled and unparalleled. Thank you profoundly.

        1. I think … I think I will expand on this. I can see part 7 benefiting from some expansion and breaking down of the code. And perhaps the others benefiting from some larger explanations. Can you pinpoint some parts where eyes start to roll and heads swim?

        2. I’d love to discuss this with you at greater length, if that’s something you’d be interested in. I have a background in developing interactive training materials and would love to throw some ideas at you. Are you local? (USA). Feel free to shoot me an email. Phone or Skype work for me.

        3. hey, ian

          my confusion started after step 4. when suddenly there is a file called “functions.php” that wasn’t introduced earlier. then i went to Google code and found that there are many, many files that are still necessary to create the theme.

          and if i remember correctly, not all those files fall in the same folder. for example, are we supposed to create a folder with the name of our theme in the “languages” folder?

          thus: i could benefit from a step 4-A, per se, a.k.a. “here is an overview of how and why our directories need to expand to accomodate our new theme.”

      3. I am having similar problems with following these steps.

        Like Sergi said the prior steps are very well explained, but I’m stuck here. You don’t say where to add things. Up to about lesson 4/5 I was right behind you, thinking I was a genuis (when really it was you) only to be crushed into the floor with this particular step.

  10. When you mention this:

    “We’ll be calling the_post() near the top of our page just after the opening of the content div and just before the navigation.”

    less experienced readers could do with a code snippet and the exact placement of this code. I figured it out by comparing with the Google Code, but I missed out on it otherwise.

    I haven’t read through the entire tutorial yet, but I think that a more complete series of code snippets, or at least at the end of each paragraph, would greatly enhance your tutorial. I guess those who are working through this are here to learn about WP templates and its most important structures. Otherwise they would just download the template as a whole and try to work in some mods the trial & error way.

    But anyway, this is a great start !

    *off to work through the rest of the tutorial.

  11. A quick question about this series: is there a print-friendly version available that removes all the comments from each lesson? I’d love to be able to follow along, but my eyes get tired when I read from the screen for too long. Thanks!

  12. Hey Ian,

    Great Tutorial!
    I think I’m actually getting this stuff.

    One question….

    After building the ‘attachment.php’ file -and verifying with Google Code – are we supposed to see the stock images show up on our pages? I’m not, and am wondering if I did something incorrectly.


  13. I’m not terribly experienced with php, and this has been helpful to a degree, but I’d suggest a little more consistency in presentation.

    For example: Much of the code has been presented in copy/paste blocks for simplicity, so I had to think for a minute before going coding the_post() myself. For single post content, you describe a simple function call, but do not mention the .entry-content div that wraps it in the Google code. You also don’t say where to place the single post title, so I had to go hunting it down in the Google code. The same goes for the entry-utility and post content code, and those two are placed in opposite succession in the final code.

    The “this is what the code should look like” blocks had been very useful to this point, and you might consider using them more often through the tutorial for better consistency and usability. You might have to break this into two pages to avoid feeling too long, but in the end it will be a much more useful tutorial.

    That’s my 2 cents. Thanks for your work, I appreciate what you’re doing and I have gotten a good amount of info from this so far.

  14. Hi Ian

    Am I right to search and replace all the ‘your-theme’ with the new name of my theme?

    1. And by the way, I agree with the other guys here wanting a more concise tutorial.

      For the first 4-5 chapters it is fine, but later I fall off, down in the jungle of code fragments, searching for the missing line “this-is-how-the-code-should-look-like”

      Anyway, thanks for making me interested in this topic. I will try to hang on as long as possible.

  15. Wow! Thanks for all of your effort Ian. I’ve been going back and forth between your tutorial and the google code page. This is an amazing resource.

  16. Nowhere in the tutorial does it say where Single Post Titles, Single Post Entry Utility Links, or Single Post Content is placed within single.php? I’ve been reading this fantastic tutorial up until now, but am completely stumped as to where this code goes.

  17. Thanks for a GREAT series on WordPress themes.

    Like a few people above, I got a bit lost on this part. But, after looking at the code on Google I think I know where I went wrong.

    My mis-understanding may lie with the ‘build as we go along approach’. For me, if you start with a block and then add lines of code throughout the article, the line numbering must be consistant. I.E. this block inserts at lines 37 – 45. or this overwrites lines 76- 82 etc. Then the reader sees where the new block goes or the lines it overwrites.
    I would also add ; – so now your code should read… for single.php and… for attachment.php etc at the end of the article (for idiots like me, who make errors and then can’t see where we went wrong).

    Please keep updating and adding to this series. (I am especially looking forward to the dynamic menu part).


  18. Hi,

    I seem to have a bit of a strange problem. I have the 404.php file uploaded, but it doesn’t show as it should. The standard error message just pops up on a blank page: “The requested URL /blahblahbla was not found on this server. ”

    The funny thing is, if i put on the default theme, which also contains a 404 page, it won’t show. Do i have some kind of server issue, or..?

    Thanks in advance..

    1. Hai folks…, Ian,
      As I understand the 404 code is supposed to be placed in the single.php (like Ian mentioned: the template-template) In that case you won’t need a 404.php file.
      I don’t know how to respond to Ian’s remark to ‘drop the navigation’ since it only has an effect on the contentpage but I don’t see anything happening on the page which doesn’t exist which is called something like: http://localhost/yourname/?p=209.
      Unfortunatly I don’t get what the attachment.php does. I don’t see anything happening anywhere and I don’t know where or what to look for.

  19. Hi All

    This is a terrific tutorial. These things are hard to write. It’s especially difficult to think of everything that people may not get. I know it been said above but to help others find it fast, there were 2 issues with this page that aren’t very clear so I thought I’d mention both in one place.

    1. After the you need to add , and
    2. You need to turn on permalinks in

  20. Okay… I think this is a great tutorial which I understood sortof up to this point… then suddenly after the title tag on the single page you lost me… You stopped saying before and after what things go and start to say things like: “remove the top navigation”… There are sections marked nav-above and nav-below, but no top navigation… I understand that people who are experienced will just get it… but I don’t… I understand HTML and CSS and would be happy with just having the php files in their compelted form… so what’s the best way for me to proceed?

  21. Wow that was a fast response. You’re the man!

    The first line that threw me was
    I accepted that a title should be above the content so i put that right after .

    Correct? no?

    actually now that I think about it Im also not sure if I have the nav-above and nav-below in the right spots… Since im not yet sure exactly what turns into what… I do preview the en result to try and check though…

    Basically everything after that is greek… what is a single post utility link and where would it logically go then?

    Then the trouble really started with duplicating the file and “First of all, delete the top navigation. ” which to me seem difficult since I can easily delete a div that is named but am not sure which divs would be inluded in this. 🙂

    Any ideas? I know Im asking for more than a simple 1 line fix.

  22. Hi. I have some trouble with the_content() function.
    Simply, the content was not displayed. Not error, not alert, just blank!

    I’m on WP 2.9

  23. Thank you for this tutorial, but I have to say, it totally goes off the rails on this page. I’m very confused.

  24. Hello,

    I have really appreciated this tutorial so far, but I’m confused about what the content of single.php should be.

    Any insight would be helpful. Thank you!

  25. Ian,

    I’m struggling with the single.php — I’ve written tutorials before so I understand that when you get into the zone sometimes you aren’t as clear about the particulars because you sort of EXPECT your audience to know SOME things automatically. 🙂

    Unfortunately, I’m lost as to where these pieces of code are placed — could you help me out?

    Thanks Ian.
    (ps. I’m on Step 7)

  26. Is not mentioned here on this tutorial page. That is why we have missed it until checking the Google code. Maybe it should be mentioned at the top.

  27. for people having problems displaying content in single.php try putting this right before everything else(i.e. the_title(), etc)

  28. This tutorial just is NOT good at all. At first you explained really well how and where those blocks of code should go but then when the tutorial went on you just started to say things like “lets put this block of code in there” ..not telling where it should go.. and now i have no idea if there is all the right stuff in the right places anymore.. really frustrating and confusing! In this page you just started saying what to do next but you stopped showing HOW TO DO IT! You just stopped the damn tutorial. Thanks for trying but thanks also for wasting my time..

    1. Pepp, don’t be rude!
      I’ve had the same problems, and I also think the tutorial could be a little easier in some sections, but when I got in trouble Ian gave some useful suggestions. I’ve checked the code he posted and I’ve finished the tutorial just fine. I’ve learned a lot in the process, and used the theme in my site. What can one ask more from someone who’s being generous enough to give his time and knowledge to the community?
      If you can’t finish it, it’s really your fault, not Ian’s.

    2. Obviously someone has never written a tutorial.

      The process is tedious, and towards then ends of them we get a little burned out. Can you blame us? People like Ian and myself (and many others) spend the entire DAY in front of the computer doing this shit — which means these tutorials come right out of our spare time, often of which there isn’t too much.

  29. I’m very confused so far until this point all tutorials are about setting up the .php files when does design part come in?? As a designer it’s hard to follow or understand…
    I’m trying to set up my own design blog it would be great if i could set my own design templets… there anything would you like to suggest for designers?? some one like me who just started to learn.. I hope i’m not the only person getting confused here….in your description you said you are a professional designer would you like to share how did you start????

    many thanks

  30. hmm.. people that having trouble with single.php need .htaccess in the top folder of wordpress installation, then change the permalink again at admin. If still error, you need to enable allowoveride the apache at /etc/httpd/conf/httpd.conf

    thank for the tutorial Ian! 😀 still reading it…. going to next page

  31. Until this step everthing was going fine, but really at this point some little things goes out of control… haha
    Despite the fact of be the best related article i’ve ever found, some points gets very unclear, especially talking bout the single.php. Im sure the newbs will get crazy and several gonna leave away.
    But for those who’re lost (like i was) the code will be a precious help. Cheers!

Comments are closed.