agentsclimarketplace

Classic theme loop template parts

Skill Lonsdale201/wp-agent-skills/theme-development/classic-theme-loop-template-parts

A community-maintained collection of agent skills for WordPress plugin and theme development.

Install
npx -y skills add Lonsdale201/wp-agent-skills --skill classic-theme-loop-template-parts

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Build or audit classic theme loops and PHP template parts for WP 7.0 without block/FSE assumptions. Covers the main Loop with `have_posts()` and `the_post()`, archive/search/single/page content choices, `get_template_part()` with `$args`, `content-none.php`, `post_class()`, secondary `WP_Query` loops with `wp_reset_postdata()`, `rewind_posts()`, pagination, single post navigation, and common bugs such as `query_posts()`, nested loops without reset, raw globals in template parts, and missing no-results states.

SKILL.md

7.9 KB, as published. Nobody here has run it

Classic Theme Loop and Template Parts

Use this when creating or reviewing the content rendering layer of a classic PHP WordPress theme: main loops, content.php style partials, archive cards, single post bodies, page bodies, empty states, and pagination.

This is not a block/FSE template-part skill. Use PHP template files and get_template_part().

When to Use This Skill

  • Writing index.php, home.php, archive.php, search.php, single.php, page.php, or 404.php.
  • Moving repeated markup into template-parts/content*.php.
  • Reviewing a nested WP_Query, featured-post section, related posts section, or custom loop.
  • Fixing pagination, no-results output, post classes, content/excerpt choice, or "wrong global post" bugs.

Main Loop Contract

For the main query, use WordPress' loop state. Do not replace it with query_posts().

<?php
get_header();
?>

<main id="main" class="site-main">
	<?php
	if ( have_posts() ) :
		while ( have_posts() ) :
			the_post();

			get_template_part( 'template-parts/content', get_post_type() );
		endwhile;

		the_posts_pagination();
	else :
		get_template_part( 'template-parts/content', 'none' );
	endif;
	?>
</main>

<?php
get_footer();

Rules:

  • Call the_post() inside the while ( have_posts() ) loop before template tags that depend on the global post.
  • Include a no-results state. template-parts/content-none.php is the standard pattern.
  • Keep the loop in template files; move per-post markup into template parts.
  • Do not call query_posts() in themes. It mutates the main query and breaks pagination and conditionals.
  • Do not manually set global $post unless a core API requires it and you fully restore state afterward.

Template Part Pattern

Use get_template_part() for repeated per-post markup:

get_template_part(
	'template-parts/content',
	get_post_type(),
	array(
		'heading_level' => is_singular() ? 'h1' : 'h2',
	)
);

get_template_part( $slug, $name, $args ) searches for:

  • {$slug}-{$name}.php
  • {$slug}.php

Inside the template part, $args is available:

<?php
$heading_level     = isset( $args['heading_level'] ) ? $args['heading_level'] : 'h2';
$allowed_headings  = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' );
$heading_level     = in_array( $heading_level, $allowed_headings, true ) ? $heading_level : 'h2';
?>

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
	<header class="entry-header">
		<?php the_title( '<' . $heading_level . ' class="entry-title">', '</' . $heading_level . '>' ); ?>
	</header>

	<div class="entry-content">
		<?php the_excerpt(); ?>
	</div>
</article>

Keep template parts presentation-focused. If a template part needs a lot of derived data, compute it before the call and pass a small $args array.

Archive vs Singular Output

Choose output by context:

  • Archives, search results, and post grids usually use the_excerpt() or a controlled custom summary.
  • single.php and page.php usually use the_content().
  • After the_content() in singular views, call wp_link_pages() so paginated posts render all pages.
  • Use the_post_navigation() for previous/next post links on single posts when the design needs it.
  • Use the_posts_pagination() for archive/search/blog pagination.

Example singular body:

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
	<header class="entry-header">
		<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
	</header>

	<div class="entry-content">
		<?php
		the_content();

		wp_link_pages(
			array(
				'before' => '<nav class="page-links" aria-label="' . esc_attr__( 'Post pages', 'textdomain' ) . '">',
				'after'  => '</nav>',
			)
		);
		?>
	</div>
</article>

Secondary Queries

For related posts, featured cards, or sidebar sections, use a separate WP_Query and always reset post data afterward.

$featured = new WP_Query(
	array(
		'post_type'           => 'post',
		'posts_per_page'      => 3,
		'ignore_sticky_posts' => true,
		'no_found_rows'       => true,
	)
);

if ( $featured->have_posts() ) :
	while ( $featured->have_posts() ) :
		$featured->the_post();
		get_template_part( 'template-parts/card', get_post_type() );
	endwhile;
endif;

wp_reset_postdata();

Rules:

  • Use wp_reset_postdata() after any custom WP_Query loop that calls $query->the_post().
  • Use no_found_rows => true when pagination is not needed.
  • Use ignore_sticky_posts => true for most curated secondary lists.
  • If you only need IDs, request IDs and render intentionally; do not run full post setup by habit.
  • Do not call wp_reset_query() unless you intentionally replaced the global query, which a theme should normally avoid.

Rewinding the Main Loop

If you read posts from the main query once and need to render the same main query again, call rewind_posts().

if ( have_posts() ) {
	the_post();
	// Inspect the first post.
}

rewind_posts();

while ( have_posts() ) {
	the_post();
	get_template_part( 'template-parts/content', get_post_type() );
}

Use this sparingly. If the first pass is only for layout decisions, prefer deriving the decision from query context instead of consuming loop state.

Required Theme Markup Hooks

Inside loop template parts:

  • Use post_class() on the root post element.
  • Use the_ID() for stable post element IDs.
  • Use the_title() with explicit wrappers rather than echoing raw title data.
  • Use the_content(), the_excerpt(), and thumbnail/template tags rather than rebuilding core output with raw fields.
  • Escape any custom meta, options, term fields, or request-derived values before output.

Review Checklist

  • Main query uses have_posts() and the_post().
  • Empty results call a real no-results template.
  • Archive/search pages do not dump full post content unless that is intentional.
  • Singular templates use the_content() and handle wp_link_pages().
  • Pagination uses the_posts_pagination() or a deliberate equivalent.
  • Secondary WP_Query loops call wp_reset_postdata().
  • No query_posts().
  • Template parts receive explicit $args when they need extra data.
  • post_class() is preserved on post wrappers.
  • No raw custom fields/options are echoed inside template parts.

Common Mistakes

  • Calling the_post() before checking have_posts() and then wondering why the first post disappears.
  • Creating content.php but forgetting content-none.php.
  • Using get_template_part() for business logic instead of presentation.
  • Passing unescaped HTML through $args and echoing it blindly.
  • Nesting a custom query inside the main loop without restoring post data.
  • Building archive pagination from $_GET['paged'] manually instead of using WordPress query state.

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.