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:
- If the front-page.php template file exists, use it
- 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
- 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.
While I understand all of this, the biggest source of confusion for me has always been in the WordPress code itself dealing with the front page. Any developer looking at just the code would assume correctly that
front-page.php
should overwrite anything on the front page. What the core code doesn’t tell you is that you need to account for both a static page and the blog posts index.It doesn’t make sense to have a conditional check for that in the theme template itself. WordPress should already be handling it. As a theme developer, you should just be writing HTML and using template tags in the templates. You shouldn’t be worrying about checks like that.
I consider this a bug in the core code logic. To me,
is_home()
should have a higher precedence thanis_front_page()
in the template hierarchy.As it stands right now,
front-page.php
should overwrite anything on the front page, regardless of the user’s settings. We know that’s wrong, but it’s what the code says. I can understand the confusion theme authors have.I do, too; and I have raised the point before. The front page template should be assumed to be a static-page template, and the template loader should account for
get_option( 'show_on_front' )
when determining whether to use the front page template or the home (blog posts index) template.I proposed putting this conditional logic into get_front_page_template(), but the idea was shot down. But, I continually see confusion over front-page handling; so maybe I’ll bring it up again at some point.
It would be great if you could get that reconsidered. It confused me to no end when I first realized that setting the “Front page displays” settings had no effect on the display of home.php and front-page.php. I had expected that setting to select one of those two templates and was including both and beating my head against a wall because i couldn’t figure out why it wasn’t working. I finally quit using front-page.php and started using a front-page-template.php instead.
now i think I like the filter option best. Thanks.
This is a topic that has caused me much confusion in the past so thank you, Chip, for making it more clear.
Your note under method two suggests that front-page.php may be omitted although I think the example code should be in front-page.php. Is your note correct?
Method 2 and 3 look the most appealing to me. Method 2 seems easier to understand but Method 3 seems easier for the developer to implement.
In Method 3, does front-page.php exist?
Yes, the example code would reside in
front-page.php
; however, if you leavefront-page.php
out altogether, you get exactly the same result: WordPress will useget_home_template()
when the front page displays the blog posts index, and will useget_page_template()
when the front page displays a static page. That happens because, withoutfront-page.php
, theelseif ( is_front_page() && $template = get_front_page_template() )
line in the template loader returns false.Yes, it does. But the filter callback causes WordPress to bypass it, by returning an empty string instead of the file name,
front-page.php
, whenis_home()
is true. So, when the blog posts index is being displayed, it is as iffront-page.php
doesn’t exist.Another bizarre idiosyncrasy to note: the front page gets a body class of “home”, while the blog index page gets “blog”
That and there is no extra class added if a
front-page.php
is available.Actually,
body_class()
adds front-page classes: either home blog if the front page displays the blog posts index, or else home page if the front page displays a static page.1st. of all, sorry for the late reply. It seems I might have overlooked this follow up. True, of course, but I like to have seen an additional body class which checks if
get_front_page_template()
is available or not.Was just playing with this today (before seeing your post) and actually came up with this solution for front-page.php:
if($wp_query->post_count == 1)
{
the_title();
the_content();
}
else if( have_posts() )
{
while( have_posts() )
{
the_post();
get_template_part( 'loop', get_post_format() );
}
}
else
{
no_results();
}
Thoughts?
This really helped to clarify my understanding!
Thank you
Chip
Under Front Page Displays…If I choose “your latest posts,” I notice the item “home” is automatically added to the menu. And in this case, home links to the blog posts and is great to use as a header menu item on the front page.
Does this mean that I do not need to set up a blog page since it automatically displays on the front page? (I only want to show posts on the front page and nowhere else)
Daniel
Correct. If your site is configured to display your blog posts index (i.e. “Your latest posts”) on the front page, then you don’t need to do any further configuration.