Page templates in subdirectories, new in WordPress 3.4

In WordPress 3.4, themes can now place page templates inside a subdirectory of their theme.

I’ve spent much of the 3.4 development cycle working on a new API called WP_Theme. But it’s not something you’ll find in the release announcement.

That’s because the vast majority of plugin and theme developers will never use it, nor should they. It’s an under-the-hood enhancement that was aimed at strengthening our internals, and it enabled us to improve quite a bit. For example, we were able to find huge performance improvements in both memory and speed. And it enhances the ability to localize themes. (More on these changes when I start working on the 3.4 field guide.)

It feels nice to be working with a modern, well-written API, even if I’m the only one using it. That’s okay, because look how easy it was to add support for page templates in a subdirectory. This is just the beginning.

Child themes can override these templates the same as before — the child theme will just need to create the same directory structure to do it. (So, /page-templates/one-column.php needs to be overridden with /page-templates/one-column.php, not /one-column.php.) And yes, we’re only looking one level down.

Updated… Caution: Renaming a page template — and that includes moving all of top-level page templates into a directory — will unassign that page template for all pages currently using it. This is a new tool in your toolbox, but use it wisely.

Published by

Andrew Nacin

Lead developer of WordPress, living in Washington, D.C. Follow me on Twitter.

84 thoughts on “Page templates in subdirectories, new in WordPress 3.4”

    1. You actually don’t have to define a directory name — we now automatically detect any page template in the theme directory or direct subdirectory.

      So /my-page-template.php will work, as will /a-subdirectory/my-page-template.php, but not /two/subdirectories/my-page-template.php.

      1. So Nacin’s point then is if the parent theme defines the template folder as page-templates then the child theme must use page-templates or can they use their own?

        In other words, if a parent theme uses page-templates as their folder containing page-blog.php, page-archives.php and then a child theme adds templates (page-404.php, page-blog.php) to its page-templates folder, will this grab all of the templates with the child page-blog.php over-writing the parent? What if a child calls their templates folder templates (and not the parents’ page-templates folder)?

        1. My point was only with regards to child theme inheritance and overriding of templates.

          A child theme can add as many page templates as it wants, in whatever directory. But if it wants to override a parent theme’s template, they need to match the directory structure for that template. page-404.php will not override a parent theme’s templates/page-404.php.

  1. I love this featured! I often use the template_include hook for custom template that put in subdirectories. In fact, using subdirectories make the theme folder clearer and keep it organized. I’m very excited about the theme API in v3.4.

  2. Having not yet investigated deeply, I’m curious how traversing directories like this impacts performance (I realize that scandir() runs optimally), however that’s quite a few additional stat calls depending on how one structures their theme no – especially considering that the entire tree must be inspected? What’s the thinking there?

    1. Sure, it’s a performance hit, but a small one. get_page_templates() is only available and used in the admin, and it’s only found on a few particular pages, such as the Pages screen and when editing or saving a page.

      In 3.3, we did a full scan of all PHP and CSS files for all themes whenever we needed the page templates for a single theme. Now we’re just looking for PHP files in the one theme we’re requesting. So while it ends up adding a few stat calls to what trunk was two days ago, they end up being minuscule in comparison to 3.3 and even in comparison to a complicated theme hierarchy.

      To display a page template, we simply pull the data from postmeta and check if the file exists. That didn’t change here; so frontend performance is still optimal.

      Additionally, there’s a hidden feature in WP_Theme: it allows for persistent caching of theme data. It’s off by default because a file edited in FTP wouldn’t refresh the cache, but if a plugin such as W3 Total Cache wanted to support it as an option (especially for sites with many themes), oh man, it sings. It’s designed so pulling a theme from cache doesn’t result in a single filesystem operation.

      So, I’m glad you asked. 🙂

    1. Yep — In basic testing (serializing everything and measuring the length of the string), it dropped to about one-third of what it was in 3.3 (and one-fifth of what it was a few years ago). But that’s not really where it shines the most. There’s no need to load all themes into memory if all you need is a few. A single theme can now be fetched wp_get_theme(), and wp_get_themes() is will know to skip themes that aren’t allowed in multisite (unless you ask it otherwise). Suddenly themes.php on WP.com got a whole lot faster. 🙂

  3. So how will this affect using get_template_directory_uri or get_template_part for example?

    Will these calls look in everything, regardless of level, after wp-content/themes/themename/

    Also, how will it affect, for example themename/sidebar-front.php or themename/templates/sidebar-front-two.php

    etc etc etc …

    1. get_template_part() only looks in the main root. This enhancement is just for “Page Templates,” which really would never be called by get_template_part().

      get_template_directory_uri() and get_stylesheet_directory_uri() will continue to return the URI to the root. If you need to access a directory, simply append it. For example, get_stylesheet_directory_uri() . ‘/images/sprite.png’.

  4. Nice dude!

    Question: Why do templates need to be static files?
    Can’t we find a way to make templates pluggable?

    IMO This feature would be revolutionary in plugin and framework dev.

  5. Hi Andrew,

    First, well done on this, it can’t have been the smallest change you’ve ever made to the core.

    Probably too forward thinking at this stage, but have you tested it for nested get_template_part() loading and variables? I only ask because on our more “CMS-y” themes, we’ve all but gotten rid of get_template_part and now use the double hit:

    include( locate_template($template . ‘.php’) );

    The move allows us to loop inside loops for Custom Content Objects, not just Custom Post Types/Formats; something that played well into folders/sub-folders.

    Just a thought, and thanks for all your work.

    Kev

    1. Hey Kev, thanks for stopping by. Unless I’m misunderstanding, there’s actually no difference between get_template_part( $template ) and include( locate_template( "$template.php" ) ). get_template_part() does support subdirectory scanning with a slug like “content/single” (it’ll look in the content directory for single.php). The behavior was accidental, but we’re keeping it.

      1. Are you sure you’re keeping that?

        Here’s from justin tadlock (http://themehybrid.com) forum:

        Let’s look at another problem with using get_
        template_part() to load files in sub-folders. In
        particular, this is a problematic line of code
        (from the core code):

        do_action( "get_template_part_{$slug}", $slug,
        $name );

        What happens when that action hook is fired
        and you’re using this code in your theme?

        get_template_part('/sub-folder/file');

        You get a hook that looks like this:

        do_action( 'get_template_part_/sub-folder/file', '/
        sub-folder/file', null );

        Not a very usuable hook.

  6. this is great. I put headers, footers and sidebars into subdirectories but got stumped on page templates. Makes for a nice, clean structure.

  7. This is the one thing I’ve been waiting years for. The ability to have page templates in sub-directories has been the one hurdle I’ve needed to jump for some uber-cool theme development.

    As always, awesome work!

    1. Ditto! I had written a patch for this back at WC Philly, but never got around to cleaning it up enough to be merged into core. Happy to see it’s being handled more efficiently!

      Nacin, how opposed are you to looping through a second directory? Any concerns? I ask because I was specifically considering /includes/templates/… as everything else in the theme is meticulously sorted within a single /includes/ folder (e.g. /styles/, /scripts/, /functions/, /widgets/, etc.) and my goal with my patch was to eventually lump /templates/ in there with them.

      This is definitely a step in the right direction, if it only ever goes one level deep I know I’ll be happier than strictly top-level.

      1. I do the same thing, except with a lib directory. Though at this point I’ll take 1 directory deep, should help keep things much cleaner moving forward with new projects.

        I for one welcome the recursive scanning overlords.

    2. I doubt we’ll ever look into levels deeper than one, as the numbers of files we could potentially end up searching exponentially grows.

      A more interesting idea would be to allow templates to be registered by plugins. And, obviously, a theme could use that as well.

      1. I’m building a WordPress framework for theme developers.
        A nice feature would be to open the WP_Theme class for extension and give a way to developers to define directories where WordPress could find/load template files so we can assigne them using the Page Attributes template feature and improve theme files organization.

  8. Interesting. So help me understand — is the only advantage of using a sub-directory for page templates is theme file *organization*? Or is there something else I’m missing, along the lines of laying groundwork for pluggable themes?

  9. Pingback: Ash Robbins
  10. I am really glad this feature is now going to be in the core. One question: what happens to pages assigned to existing templates? E.g. If I have a page assigned to login-template.php, then I move login-template.php to a folder called templates, will the existing page switch over seamlessly?

  11. Does this only apply to pages?

    I just download 3.4b3, normal index.php in theme root.. I put single.php and page.php within a sub dir but those aren’t picked up… still seeing index.php showing.

    1. I was also really hoping this would work. It would be great to be able to organize ALL page templates into a separate directory, including page.php, front-page.php, etc.

      Is there a chance for supporting this in future releases?

  12. Hi Nancin,

    I’ve seen that you recommend the All In One SEO Pack Pro Plugin. Do you think it is necessary to run on a new blog?

    Thank you,
    Michael

  13. Wow… so, how awesome is this because I’ve spent the last two days trying to get it to work. When is 3.4 releasing? 🙂

    I agree with most of the other comments… I think this is definitely “new feature announcement” worthy! It’s going to revolutionize how organized we can be as developers out of the gate!

  14. Hi, thank you for sharing knowledge 🙂

    What about sidebars? I think it should be good idea to have also sidebars in separate folder, for example theme-root/sidebars/sidebar-right.php,theme-root/sidebars/sidebar-left.php,…

    Is it possible?

  15. We upgraded to WP3.4 yesterday and now suddenly I am unable to select a custom page template from the “Page Attributes”-box on the right. The drop-down that used to be there is gone (I assume this is the behavior when WP can’t find any custom page templates?).

    If I click “Quick Edit” in the list, however, the “Template” drop-down is visible – but the only thing in it is “Default template” – none of the several custom page templates I had set up.

    What’s really weird is that pages already assigned to custom page templates still render with those templates – even though in “Quick Edit” it says “Default Template”.

    Is anyone else experiencing this problem? Do you know how to solve it? Is it a WP bug?

    1. Well we solved it. Apparently from version 3.4 you _have to_ put the Template Name-comment on its own line, like this:

      /*
      Template Name: MyTemplate
      */

      Whereas before you could do:

      /* Template Name: MyTemplate */

      Strange…

  16. Great work!

    Do you have an example on how to include the custom page template from a subfolder? When I try it breaks my options page, so I must be doing something wrong…

    Cheers

  17. Hi Andrew,

    One of the features I love about WP is that I don’t have to write any conditional statements to assign the correct template to the content. So I often end up having things like page-contact.php and front-page.php in my themes, it’s a great time saver.

    I heard it’s possible to move those page templates in 3.4 to sub-directories, but no matter what I tried (dir name: /pages/ or /page-templates/, template file page-contact.php, contact.php) WordPress won’t include those templates automatically… Am I missing something here, or is this not implemented yet?

    For reference: http://codex.wordpress.org/File:Template_Hierarchy.png?utm_source=wordpress-modguide

  18. This is brilliant but I have noticed a bug in that the body_class() misses a space between the classes and the page template name so instead of having I get they are missing a space.

  19. It has stripped out the html so I’ll do it simply the class on the body outputs as “page-template-custom page-templates default-with-2-products-php” instead of “page-template-custompagetemplatesdefault-with-2-products-php” they are missing a space.

  20. Next step: being able to put template overrides in a subdirectory? I would kill for that! (e.g. archive-mytype.php and single-mytype.php) To keep different types organized.

    1. +1 Yes Yes Yes. When I first heard about this feature I mistakenly assumed that it applied to these default templates as well. I’m obsessed with keeping theme files organized and this would be a great next step. I don’t see why it shouldn’t be possible.

  21. I’m seeing the same behavior as noted by Andreas L on June 14, 2012 at 1:37 am — There is no place to select ANY template in the“Page Attributes”-box on the right.

    If I click “Quick Edit” in the list, however, the “Template” drop-down is visible – but the only thing in it is “Default template” – not my custom template.

    These are the first 5 lines of my page template file:

    How do I make the page template choices drop down appear on my New page screen?

    thanks,

  22. First 5 didn’t post so I’ll try to explain:

    Line 1: is the opening php tag less than – question mark -php (no dashes)
    Line 2: /*
    Line 3: Template Name: BluesD_01
    Line 4: */
    Line 5: closing php tag question mark – greater than (no dashes)

  23. 男士皮带是男士腰间的品位象征。

    现代都市生活,似乎让男人承受的压力越来越大。从男人腰间的皮带上,就能体会到其忙碌的状态。他们总是挂着或别着手机、钥匙包,甚至打火机,这很容易使人联想到古人腰带上携挂着的弓、剑、砾石、算盘等。挂弓、剑等在古代是高地位的象征,也必然会受到别人的羡慕。可是在今天,如果一个男人再在腰上挂一大串东西的话,那么就显得没有品味没有内涵了。现实生活中,大多数男人也都会不以为然地在腰间挂一些东西,甚至还美其名曰是体现自己的身份,殊不知这样做,在别人眼里,尤其是在一些有品位有见识的女性眼里,这些举止看起来是多么俗气和没有档次。

    一个成熟的男人是不会让自己的腰间挂满着细小东西的。他们会在腰间系上一条高雅的腰带,简单而干练,还能在一定程度上代表男人的身份、品味和个性。

    男人该如何选择适合自己又彰显品位的皮带呢?
    1.在皮带的选择上请千万保持低调。黑色、咖色或棕色的皮带配以钢质、金质或银质的皮带扣,既适合各种衣物和场合,又可以很好地表现职业男士的气质。不要轻易使用式样新奇的和配以巨大皮带扣的皮带。

    2.要考虑皮带的装饰性,不要挂过多的物品。因为简洁、干练是男人的特征。

    3.皮带的长度应介于第一和第二的裤扣之间,宽度应保持在3厘米。如果皮带太窄,会失去男性的阳刚之气;如果皮带太宽,则只适合于休闲、牛仔风格的装束。

    总之,在时尚潮流中,一个注意风度形象的男人,总会在腰间这一细节上多花心思,而不是随意对待,让一些小饰物挡住皮带的光芒。

  24. Hi Nacin,

    Before the WP_Theme class (as is implemented outside the wp-admin), I could add filters to get_template_directory() and similar functions to make WP search for a custom folder for pages and page templates, whereas using this new implementation, I saw that is impossible to add custom folders (filters or override methods), it only searches at one level down into the stylesheet folder.

    Is there any way to make it search into a custom folder for pages/page_templates other that the theme stylesheet + 1 level deep using the WP_Theme class?

    Thanks
    Romulo

    1. Hi,

      I was also looking for a solution about defining directories where to load Template pages… but out of luck until now. Btw, which filter did you use with “get_template_directory” in order to define a custom folder inside your theme ?

      Thanks

      1. Hi Julien,

        If you check the get_template_directory(), get_template_directory_uri() functions: (https://github.com/WordPress/WordPress/blob/master/wp-includes/theme.php#L251-L273), you can check the return being filtered.
        This also applies to get_stylesheet_directory() and get_stylesheet_directory_uri()

        There is another problem, because the locate_template() function is still using two deprecated constants into it instead of get_template_directory() and get_stylesheet_directory(). Look at this issue for the fix (http://core.trac.wordpress.org/ticket/18298), but it wasn’t merged to the core yet.

        Also about this subject, Justin Tadlock made a post really interesting about loading templates as well: http://justintadlock.com/archives/2010/11/17/how-to-load-files-within-wordpress-themes

        Thanks
        Romulo

  25. Hi !
    Is there a nice way to override the get_page_templates() function so we can change the depth ?
    I can see in the get_page_templates() function in the Class : class-wp-theme.php.
    Line 932 : $files = (array) $this->get_files( ‘php’, 1 );
    We just have to replace 1 by -1.
    But how can i override it in a nice way ? Without interfering with future updates ?

    Thanks.

Comments are closed.