Change WooCommerce Add to Cart Text if Product in Cart

On a WooCommerce shop, customers often click Add to cart a second time because they forgot the product is already in the cart, and end up with two. This snippet changes the WooCommerce add to cart text to Add again? when the product is already in the cart, on the single product page and in shop, category and product block loops.

The idea comes from Business Bloomer, which has many more WooCommerce snippets.

The snippet

<?php
/**
 * WooCommerce: change the Add to cart button text when the product is already in the cart.
 */

// ===== Config =====
function dpc_wc_in_cart_button_text(): string {
	return __( 'Add again?', 'dpc' );
}
// ===== End of config =====

add_filter( 'woocommerce_product_single_add_to_cart_text', 'dpc_wc_in_cart_button', 20, 2 );
add_filter( 'woocommerce_product_add_to_cart_text', 'dpc_wc_in_cart_button', 20, 2 );

function dpc_wc_in_cart_button( $text, $product = null ) {
	if ( ! $product instanceof WC_Product ) {
		return $text;
	}

	// In shop and category loops only simple products are added straight to the cart.
	// Variable, grouped and out-of-stock products keep "Select options" or "Read more".
	if ( 'woocommerce_product_add_to_cart_text' === current_filter()
		&& ! ( $product->is_type( 'simple' ) && $product->is_purchasable() && $product->is_in_stock() ) ) {
		return $text;
	}

	return dpc_wc_product_in_cart( $product->get_id() ) ? dpc_wc_in_cart_button_text() : $text;
}

function dpc_wc_product_in_cart( int $product_id ): bool {
	// There is no cart in wp-admin, REST and block editor requests, cron or WP-CLI.
	if ( ! did_action( 'wp_loaded' ) || ! function_exists( 'WC' ) || null === WC()->cart ) {
		return false;
	}

	foreach ( WC()->cart->get_cart() as $item ) {
		if ( $product_id === (int) $item['product_id'] || $product_id === (int) $item['variation_id'] ) {
			return true;
		}
	}

	return false;
}

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor, edit the config and publish. Leave Action/Hook empty. Shop pages, the cart fragments request and the product blocks all render buttons, so the code has to load everywhere.

The same code also works unchanged in a small plugin or an mu-plugin.

How it works

  • WooCommerce passes the button text through woocommerce_product_single_add_to_cart_text on product pages and woocommerce_product_add_to_cart_text in loops and blocks. Both filters also pass the product, so one function handles both.
  • dpc_wc_product_in_cart() loops over the cart and compares each item’s product_id and variation_id with the product. A variable product counts as in the cart when any of its variations is.
  • In loops only simple, purchasable products in stock are changed. Variable and grouped products keep Select options, and out-of-stock products keep Read more.
  • The cart only exists on front-end requests. In wp-admin, the REST API, the block editor, cron and WP-CLI, WC()->cart is null, and the function returns the normal text.

The older version called WC()->cart->get_cart() without that check, which is a fatal error wherever the cart is missing, for example when the block editor previews a product block. It also compared the cart with get_the_ID(), the current post, so in product blocks and custom loops it often checked the wrong product.

Customise it

  • Other wording: change the text in dpc_wc_in_cart_button_text(), for example __( 'In cart: add one more', 'dpc' ).
  • Only on product pages: remove the woocommerce_product_add_to_cart_text line.
  • For a different button text when the product is not in the cart, see Change Add to Cart Button Text in WooCommerce.

Gotchas

  • Full-page caching serves the same HTML to everyone. Make sure your cache skips visitors with items in the cart (the woocommerce_items_in_cart cookie); most WooCommerce-aware caches do.
  • After an AJAX add to cart in a shop loop, WooCommerce shows View cart next to the button but does not change the button text until the page reloads.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.