Template Hierarchy

Posts filed under Template Hierarchy

Home Page and Front Page and Templates, Oh My!

Filed in Web DevelopmentTags: Template Hierarchy, Themes, WordPress

Since the introduction of the front-page.php template file in WordPress 3.0 over three years ago, there remains a great deal of confusion about the proper implementation of custom/static site front pages in WordPress. This post will address that confusion, and explain the proper implementation of the custom/static site front page feature in WordPress.

Nomenclature

The first area of confusion involves nomenclature. In most contexts, "home page" refers to the site front page; however, in WordPress, the "home" page is not the site front page, but rather the Blog Posts Index page. WordPress refers to the site front page as the Front Page.  This nomenclature applies in several areas. For example:

Query conditionals:

  • is_front_page() - returns true when the stie front page is being displayed, whether the site front page is set to display the blog posts index or a static page
  • is_home() - returns true when the blog posts index is being displayed, whether the blog posts index is displayed on the site front page or on a static page

Template file names:

  • front-page.php - used to display the site front page, whether the site front page is set to display the blog posts index or a static page
  • home.php - used to display the blog posts index, whether the blog posts index is displayed on the site front page or on a static page
  • index.php - used as the default fallback template file for all contexts

Unfortunately, this nomenclature isn't consistent throughout WordPress core. For example, the body_class() template tag adds the following classes:

  • Front Page: home
  • Blog Posts Index: blog

(Long term, it would possibly be wise for WordPress core to deprecate the "home" nomenclature in favor of "blog" nomenclature - i.e. an is_blog() conditional and a blog.php template file - but that's a discussion for another day.)

The key point here is: in WordPress parlance: home means blog posts index, and not site front page.

Creating a Static Front Page

WordPress core has a specific method for implementing a static front page. Themes need to account for the core implementation, rather than implement a non-core method for displaying a static front page and displaying the blog posts index on a separate static page.

The core method involves creating two static pages, and configuring the front page options under Settings -> Reading.

  • The "Front page displays" option correlates to get_option( 'show_on_front' ), which returns either 'page' if "Front page displays" is set to "A static page", or 'posts' if "Front page displays" is set to "Your latest posts".
  • The "Front page" option is available if "Front page displays" is set to "A static page", and correlates to get_option( 'page_on_front' ), which returns the ID of the page assigned to "Front page"
  • The "Posts page" option is available if "Front page displays" is set to "A static page", and correlates to get_option( 'page_for_posts' ), which returns the ID of the page assigned to "Posts page"

When a user sets these options, it is reasonable and expected for a Theme to respect/incorporate those option settings in the Theme's template files.

Template Hierarchy

When developing a WordPress Theme, understanding the Template Hierarchy is critical to ensuring proper use of Theme template files for the site front page and blog posts index contexts. For the site front page, there are basically three rules:

  1. If the front-page.php template file exists, use it
  2. If the front-page.php template file does not exist, and the front page is set to display the blog posts index, use the Home Template hierarchy
  3. If the front-page.php template file does not exist, and the front page is set to display a static page, use the Page Template hierarchy

To understand how this works, we need to dig a bit into the Template Hierarchy, which is defined in core via wp-includes/template-loader.php. The meat of the code is here:

if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
	$template = false;
	if     ( is_404()            && $template = get_404_template()            ) :
	elseif ( is_search()         && $template = get_search_template()         ) :
	elseif ( is_tax()            && $template = get_taxonomy_template()       ) :
	elseif ( is_front_page()     && $template = get_front_page_template()     ) :
	elseif ( is_home()           && $template = get_home_template()           ) :
	elseif ( is_attachment()     && $template = get_attachment_template()     ) :
		remove_filter('the_content', 'prepend_attachment');
	elseif ( is_single()         && $template = get_single_template()         ) :
	elseif ( is_page()           && $template = get_page_template()           ) :
	elseif ( is_category()       && $template = get_category_template()       ) :
	elseif ( is_tag()            && $template = get_tag_template()            ) :
	elseif ( is_author()         && $template = get_author_template()         ) :
	elseif ( is_date()           && $template = get_date_template()           ) :
	elseif ( is_archive()        && $template = get_archive_template()        ) :
	elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
	elseif ( is_paged()          && $template = get_paged_template()          ) :
	else :
		$template = get_index_template();
	endif;
	if ( $template = apply_filters( 'template_include', $template ) )
		include( $template );
	return;
endif;

 

Basically, this code steps through each of the query context conditionals in a specific order, and defines the template to use for the first one that returns true.

For our purposes, the contexts we are concerned with are Front Page, Home, and Page, which are evaluated in the following order (with conditionals in-between omitted):

elseif ( is_front_page()     && $template = get_front_page_template()     ) :
elseif ( is_home()           && $template = get_home_template()           ) :
elseif ( is_page()           && $template = get_page_template()           ) :

 

Translated, this says:

  • If a valid Front Page template file exists, use it
  • Otherwise, if this is the blog posts template and a valid blog posts index template file exists, use it
  • Otherwise, if this is a static page and a valid page template file exists, use it
  • Otherwise, use the default template file

To understand further what core is doing here, we need to look at get_front_page_template(), get_home_template(), and get_page_template(), which are defined in wp-includes/template.php:

get_front_page_template() looks for

  • front-page.php

get_home_template() looks for:

  • home.php

get_page_template() looks for

  • _wp_page_template meta-key value (user-assigned custom page template)
  • page-{slug}.php
  • page-{id}.php
  • page.php

Which Template File?

So understanding the hierarchy, it should be easy to determine which template file to create for each given context.

Blog Posts Index

Use home.php.

Simple, right? But what about a custom page template, such as template-blog.php? Can't a Theme also use that? Not to put too fine a point on it, but: no. That would be _doing_it_wrong(). Using a custom page template for the blog posts index, either:

  • Won't work, because when implemented properly, a static page as custom page template simply won't ever use a custom page template
  • Would require the Theme to force the user to use a method of assigning the blog posts index that does not conform to the WordPress core method
  • Would cause pagination issues when rendering the blog posts index using the custom page template, because WordPress recognizes the main query for the custom page template to be the static page post object, rather than the blog posts index

So, while technically you can use a custom page template to display the blog posts index, in doing so you'll be forcing your Theme users to use a non-standard way of assigning the static page used to display the blog posts index, and will cause yourself headaches in getting the custom page template to function properly - all of which is easily avoided simply by using home.php instead.

Site Front Page

Use front-page.php.

But there are some caveats - namely, because front-page.php takes precedence on the site front page regardless of what the user has configured the front page to display, the Theme needs to account for the front page displaying either a static page or the blog posts index. There are a few methods to do so.

Conditional output in front-page.php

The first method uses a conditional statement inside front-page.php, that evaluates get_option( 'show_on_front' ), and either includes get_home_template(), or custom front-page markup:

if ( 'posts' == get_option( 'show_on_front' ) ) {
    include( get_home_template() );
} else {
    // Custom content markup goes here
}

 

This method is useful if the Theme already includes a front-page.php template file, and you need to implement an easy way to account for the blog posts index-as-front-page use case.

Conditional output in front-page.php, with a custom page template

The second method is a variation of the first, that includes get_page_template() instead of hard-coded custom front-page markup inside of front-page.php:

if ( 'posts' == get_option( 'show_on_front' ) ) {
    include( get_home_template() );
} else {
    include( get_page_template() );
}

 

This method is useful if the Theme includes a featured-content (or perhaps a portfolio-style) custom page template that isn't necessarily intended to be used as the site front page. When using this method, the user will need to apply the custom page template to the page assigned to display the site front page. This is an extra step for the user, but also gives the user the flexibility to use the featured-content custom page template on any static page, rather than only as the site front page.

Note also: if using this method, you can also safely omit front-page.php entirely, as it is technically unnecessary.

Filter front_page_template

The third method uses front-page.php solely as custom static-page-as-front-page content, and filters front_page_template when the front page is set to display the blog posts index, causing the Template Hierarchy to ignore front-page.php and fall back to get_home_template(), via callback in functions.php:

function themeslug_filter_front_page_template( $template ) {
    return is_home() ? '' : $template;
}
add_filter( 'front_page_template', 'themeslug_filter_front_page_template' );

 

To see how this method works, we need to take another look at get_front_page_template():

function get_front_page_template() {
	$templates = array('front-page.php');

	return get_query_template( 'front_page', $templates );
}

 

The $templates array is passed through get_query_template():

function get_query_template( $type, $templates = array() ) {
	$type = preg_replace( '|[^a-z0-9-]+|', '', $type );

	if ( empty( $templates ) )
		$templates = array("{$type}.php");

	return apply_filters( "{$type}_template", locate_template( $templates ) );
}

 

And here you can see the filter being applied:

return apply_filters( "{$type}_template", locate_template( $templates ) );

 

For the front page context, $type = "front-page", resulting in the filter front_page_template.

By returning an empty string for front_page_template, the Template Hierarchy will evaluate this line as false:

elseif ( is_front_page()     && $template = get_front_page_template()     ) :

 

Causing it to move on to the blog posts index line:

elseif ( is_home()           && $template = get_home_template()           ) :

 

This is probably the simplest method to implement. (H/T Justin Tadlock)

EDIT:

There is a bug in WordPress, that causes the front_page_template filter name to be truncated as frontpage_template. So, use this instead:

function themeslug_filter_front_page_template( $template ) {
    return is_home() ? '' : $template;
}
add_filter( 'frontpage_template', 'themeslug_filter_front_page_template' );

 

Questions? Use cases unaccounted for? Let me know in the comments.