WooCommerce: Inject Ad After the nth Product @ Shop Page

Is it possible to insert a custom loop item, such as a promotional banner or a “fake” product, into the WooCommerce shop page after a specific number of real products – for example, after the third one?

Spoiler alert: yes, it is!

This won’t alter the results counter though (“Showing 1–12 of 10138 results“), and only works on the “classic” Shop Page, but we can live with that.

In this post, we’ll explore a simple way to inject custom HTML into the shop loop at a specific position, without affecting pagination or breaking layout. This solution is ideal for inserting ads, upsells, or custom notices directly in the WooCommerce product grid. Enjoy!

Yep, that’s not a WooCommerce product!

PHP Snippet: Display a Fake Product After a Specific Product in the WooCommerce Shop Loop

This snippet adds a promotional block to the WooCommerce shop page, placing it exactly after the third product in the loop.

It hooks into woocommerce_after_shop_loop_item, which runs after each product’s content is output. The function checks the current loop index, and when it reaches 3 (i.e. after three products), it outputs a custom <li> element styled like a product. This includes an image, a title (“Promotion”), and a message (“Win a free tablet!”).

It also handles WooCommerce layout classes like first and last to keep the grid aligned correctly. The loop counter is manually increased to ensure the layout doesn’t break.

This is a clean way to inject non-product content into the grid without modifying the product query itself — perfect for special offers, ads, or custom calls to action.

/**
 * @snippet       Display Ad After 3rd Product @ WooCommerce Shop Loop
 * @tutorial      https://businessbloomer.com/woocommerce-customization
 * @author        Rodolfo Melogli, Business Bloomer
 * @compatible    WooCommerce 9
 * @community     https://businessbloomer.com/club/
 */

add_action( 'woocommerce_after_shop_loop_item', 'bbloomer_display_fake_product_position_4', 9999 );

function bbloomer_display_fake_product_position_4() {
	$loop_index = wc_get_loop_prop( 'loop', 0 );
	$columns = absint( max( 1, wc_get_loop_prop( 'columns', wc_get_default_products_per_row() ) ) );
	if ( $loop_index === 3 ) {
		wc_set_loop_prop( 'loop', $loop_index + 1 );
		$loop_index = wc_get_loop_prop( 'loop', 0 );
		if ( 0 === ( $loop_index - 1 ) % $columns || 1 === $columns ) {
			$class = ' first';
		} elseif ( 0 === $loop_index % $columns ) {
			$class = ' last';
		} else $class = '';
		echo '</li>';		
		echo '<li class="product' . $class . '">';
      echo '<a href="https://example.com" target="_blank">';
      echo '<img src="/wp-content/uploads/2025/04/special-offer.png">';
      echo '<h2 class="woocommerce-loop-product__title">Promotion</h2>';
		echo '<span class="price">Win a free tablet!</span>';
      echo '</a>';
	}
}

Where to add custom code?

You should place custom PHP in functions.php and custom CSS in style.css of your child theme: where to place WooCommerce customization?

This code still works, unless you report otherwise. To exclude conflicts, temporarily switch to the Storefront theme, disable all plugins except WooCommerce, and test the snippet again: WooCommerce troubleshooting 101

Related content

Rodolfo Melogli

Business Bloomer Founder

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as an independent WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza. Follow @rmelogli

Questions? Feedback? Customization? Leave your comment now!
_____

If you are writing code, please wrap it like so: [php]code_here[/php]. Failure to complying with this, as well as going off topic or not using the English language will result in comment disapproval. You should expect a reply in about 2 weeks - this is a popular blog but I need to get paid work done first. Please consider joining the Business Bloomer Club to get quick WooCommerce support. Thank you!

Your email address will not be published. Required fields are marked *