Overview
Template parts are modular PHP files used to display different types of content. They promote code reusability and follow WordPress template hierarchy conventions.
Location: template-parts/
Zalbi Theme includes four template parts:
content.php - Standard post display
content-page.php - Page content display
content-search.php - Search result items
content-none.php - No content found message
Loading Template Parts
get_template_part()
Template parts are loaded using WordPress’s get_template_part() function:
get_template_part( 'template-parts/content', get_post_type() );
Common Usage Patterns
In Loop
Specific Type
Fallback
<?php
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/content', get_post_type() );
endwhile;
else :
get_template_part( 'template-parts/content', 'none' );
endif;
?>
// Loads template-parts/content-page.php
get_template_part( 'template-parts/content', 'page' );
// Loads template-parts/content-search.php
get_template_part( 'template-parts/content', 'search' );
// Try content-{post-type}.php, fallback to content.php
get_template_part( 'template-parts/content', get_post_type() );
content.php
Standard Post Template
Displays regular blog posts with full metadata.
File: template-parts/content.php
Used for: Standard posts, custom post types (fallback)
Structure
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
<div class="entry-meta">
<?php
zalbi_posted_on(); // Date
zalbi_posted_by(); // Author
?>
</div>
</header>
<?php zalbi_post_thumbnail(); ?>
<div class="entry-content">
<?php the_content(); ?>
<?php wp_link_pages(); ?>
</div>
<footer class="entry-footer">
<?php zalbi_entry_footer(); ?>
</footer>
</article>
Features
- Dynamic Title Tag: Uses
<h1> on singular pages, <h2> on archives
- Post Metadata: Shows publish date and author for posts
- Featured Image: Displays via
zalbi_post_thumbnail()
- Entry Footer: Categories, tags, edit link via
zalbi_entry_footer()
- Pagination: Multi-page post support with
wp_link_pages()
Title Logic
if ( is_singular() ) :
the_title( '<h1 class="entry-title">', '</h1>' );
else :
the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
endif;
On archive pages, titles link to the full post. On single pages, they’re plain text.
if ( 'post' === get_post_type() ) :
?>
<div class="entry-meta">
<?php
zalbi_posted_on();
zalbi_posted_by();
?>
</div>
<?php
endif;
Metadata only displays for the post post type, not pages or custom post types.
content-page.php
Page Content Template
Simplified template for static pages.
File: template-parts/content-page.php
Used for: Pages, custom page templates
Structure
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
</header>
<?php zalbi_post_thumbnail(); ?>
<div class="entry-content">
<?php
the_content();
wp_link_pages();
?>
</div>
<?php if ( get_edit_post_link() ) : ?>
<footer class="entry-footer">
<?php edit_post_link(); ?>
</footer>
<?php endif; ?>
</article>
Differences from content.php
| Feature | content.php | content-page.php |
|---|
| Post Meta | Shows date/author | None |
| Entry Footer | Categories, tags, comments | Edit link only |
| Title | Conditional h1/h2 | Always h1 |
| Use Case | Blog posts | Static pages |
Edit Link
Only shown to users with edit permissions:
<?php if ( get_edit_post_link() ) : ?>
<footer class="entry-footer">
<?php
edit_post_link(
sprintf(
__( 'Edit <span class="screen-reader-text">%s</span>', 'zalbi' ),
wp_kses_post( get_the_title() )
),
'<span class="edit-link">',
'</span>'
);
?>
</footer>
<?php endif; ?>
content-search.php
Search Results Template
Optimized display for search result listings.
File: template-parts/content-search.php
Used for: Search results page (search.php)
Structure
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
<?php if ( 'post' === get_post_type() ) : ?>
<div class="entry-meta">
<?php
zalbi_posted_on();
zalbi_posted_by();
?>
</div>
<?php endif; ?>
</header>
<?php zalbi_post_thumbnail(); ?>
<div class="entry-summary">
<?php the_excerpt(); ?>
</div>
<footer class="entry-footer">
<?php zalbi_entry_footer(); ?>
</footer>
</article>
Key Features
- Linked Titles: All titles link to full content
- Excerpt Display: Uses
the_excerpt() instead of full content
- Post Metadata: Shows for posts, hidden for pages
- Footer Info: Categories, tags, comments
Excerpt vs Content
// content.php uses full content
<div class="entry-content">
<?php the_content(); ?>
</div>
// content-search.php uses excerpt
<div class="entry-summary">
<?php the_excerpt(); ?>
</div>
Search results show excerpts to provide quick scanning of multiple results.
content-none.php
No Content Template
Displays helpful messages when no posts are found.
File: template-parts/content-none.php
Used for: Empty archives, failed searches, empty blogs
Structure
<section class="no-results not-found">
<header class="page-header">
<h1 class="page-title"><?php esc_html_e( 'Nothing Found', 'zalbi' ); ?></h1>
</header>
<div class="page-content">
<?php
if ( is_home() && current_user_can( 'publish_posts' ) ) :
// Message for admins on empty blog
elseif ( is_search() ) :
// Message for failed search
else :
// Generic no content message
endif;
?>
</div>
</section>
Conditional Messages
Empty Blog (Admin)
Failed Search
Generic Empty
if ( is_home() && current_user_can( 'publish_posts' ) ) :
printf(
'<p>' . __( 'Ready to publish your first post? <a href="%1$s">Get started here</a>.', 'zalbi' ) . '</p>',
esc_url( admin_url( 'post-new.php' ) )
);
Shows link to create first post (admins only).elseif ( is_search() ) :
?>
<p><?php esc_html_e( 'Sorry, but nothing matched your search terms. Please try again with some different keywords.', 'zalbi' ); ?></p>
<?php
get_search_form();
Shows search form for retry.else :
?>
<p><?php esc_html_e( 'It seems we can’t find what you’re looking for. Perhaps searching can help.', 'zalbi' ); ?></p>
<?php
get_search_form();
General empty state with search.
Custom Template Functions
zalbi_post_thumbnail()
Displays featured images with conditional markup.
Location: inc/template-tags.php:122
function zalbi_post_thumbnail() {
if ( post_password_required() || is_attachment() || ! has_post_thumbnail() ) {
return;
}
if ( is_singular() ) :
?>
<div class="post-thumbnail">
<?php the_post_thumbnail(); ?>
</div>
<?php
else :
?>
<a class="post-thumbnail" href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
<?php
the_post_thumbnail(
'post-thumbnail',
array(
'alt' => the_title_attribute( array( 'echo' => false ) ),
)
);
?>
</a>
<?php
endif;
}
zalbi_posted_on()
Displays formatted post date.
Location: inc/template-tags.php:14
function zalbi_posted_on() {
$time_string = '<time class="entry-date published updated" datetime="%1$s">%2$s</time>';
if ( get_the_time( 'U' ) !== get_the_modified_time( 'U' ) ) {
$time_string = '<time class="entry-date published" datetime="%1$s">%2$s</time><time class="updated" datetime="%3$s">%4$s</time>';
}
$posted_on = sprintf(
esc_html_x( 'Posted on %s', 'post date', 'zalbi' ),
'<a href="' . esc_url( get_permalink() ) . '" rel="bookmark">' . $time_string . '</a>'
);
echo '<span class="posted-on">' . $posted_on . '</span>';
}
zalbi_posted_by()
Displays post author with link to archive.
Location: inc/template-tags.php:43
function zalbi_posted_by() {
$byline = sprintf(
esc_html_x( 'by %s', 'post author', 'zalbi' ),
'<span class="author vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a></span>'
);
echo '<span class="byline"> ' . $byline . '</span>';
}
Displays categories, tags, comments, and edit links.
Location: inc/template-tags.php:59
function zalbi_entry_footer() {
// Categories (posts only)
if ( 'post' === get_post_type() ) {
$categories_list = get_the_category_list( esc_html__( ', ', 'zalbi' ) );
if ( $categories_list ) {
printf( '<span class="cat-links">' . esc_html__( 'Posted in %1$s', 'zalbi' ) . '</span>', $categories_list );
}
// Tags
$tags_list = get_the_tag_list( '', esc_html_x( ', ', 'list item separator', 'zalbi' ) );
if ( $tags_list ) {
printf( '<span class="tags-links">' . esc_html__( 'Tagged %1$s', 'zalbi' ) . '</span>', $tags_list );
}
}
// Comments link
if ( ! is_single() && ! post_password_required() && ( comments_open() || get_comments_number() ) ) {
echo '<span class="comments-link">';
comments_popup_link();
echo '</span>';
}
// Edit link
edit_post_link();
}
Creating Custom Template Parts
Example: Content Part for Custom Post Type
Create template-parts/content-hinchable.php:
<?php
/**
* Template part for displaying hinchables in archive
*/
?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'product-card' ); ?>>
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail( 'medium_large' ); ?>
</a>
<div class="card-content">
<?php
$tag_color = get_field('etiqueta_color');
$medidas = get_field('medidas');
$capacidad = get_field('capacidad');
?>
<span class="card-tag <?php echo esc_attr( $tag_color ); ?>">
<?php echo esc_html( get_field('categoria_nombre') ); ?>
</span>
<h3 class="card-title">
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</h3>
<ul class="card-specs">
<li><i class="fas fa-ruler-combined"></i> <?php echo esc_html( $medidas ); ?></li>
<li><i class="fas fa-users"></i> <?php echo esc_html( $capacidad ); ?></li>
</ul>
</div>
</article>
Loading Custom Template Part
In archive-hinchable.php:
<?php
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/content', 'hinchable' );
endwhile;
endif;
?>
Best Practices
- Use
content-{type}.php pattern
- Match post type slugs for automatic loading
- Use descriptive names:
content-event, not content-1
- Template parts run in The Loop context
- Global
$post is available
- Use
get_the_ID() for explicit post ID
- Always escape output
- Keep template parts focused on presentation
- Move complex logic to functions
- Use template functions from
inc/template-tags.php
- Support custom post types with fallbacks
Common Modifications
Remove Post Metadata
In template-parts/content.php:21-29, comment out:
<?php // if ( 'post' === get_post_type() ) : ?>
<!-- <div class="entry-meta">
<?php
// zalbi_posted_on();
// zalbi_posted_by();
?>
</div> -->
<?php // endif; ?>
Change Excerpt Length
Add to functions.php:
function zalbi_custom_excerpt_length( $length ) {
return 20; // Number of words
}
add_filter( 'excerpt_length', 'zalbi_custom_excerpt_length', 999 );
Add Custom Wrapper
In any template part:
<article <?php post_class( 'my-custom-class' ); ?>>
<!-- content -->
</article>
post_class() automatically adds WordPress classes plus your custom ones.
Passing Variables to Template Parts
Use get_template_part() with set_query_var():
// Set variable
set_query_var( 'card_style', 'compact' );
get_template_part( 'template-parts/content', 'product' );
// In template-parts/content-product.php
$card_style = get_query_var( 'card_style', 'default' );
Or use the modern approach (WordPress 5.5+):
get_template_part(
'template-parts/content',
'product',
array( 'card_style' => 'compact' )
);
// In template part
$args = wp_parse_args( $args, array( 'card_style' => 'default' ) );