Skip to content
ThemesIonic — home
WooCommerce

Adding Content Next to the WooCommerce Add to Cart Button

The hook fires inside the cart form, which is the whole point: an input printed here is submitted with the add-to-cart request. It is also why a stray closing tag printed here breaks the button.

Updated 4 min read intermediate

Output from this hook is inside the <form class="cart">, and that is the feature. woocommerce_after_add_to_cart_button fires immediately after the submit button in the add-to-cart templates for simple, variable, grouped and external products. An <input> printed here is a form field: it travels with the add-to-cart request and is available to the hooks that build the cart item. A </div> printed here is also inside the form, which is why a template fragment pasted into this hook so often produces a button that stops working.

Minimal example that reaches the cart

Three hooks, in order — print the field, store the value, display it:

add_action( 'woocommerce_after_add_to_cart_button', 'ti_gift_note_field' );

function ti_gift_note_field() {
	echo '<p class="ti-gift-note"><label>' . esc_html__( 'Gift note', 'ti' )
		. ' <input type="text" name="ti_gift_note" maxlength="120"></label></p>';
}

add_filter( 'woocommerce_add_cart_item_data', 'ti_gift_note_store', 10, 2 );

function ti_gift_note_store( $cart_item_data, $product_id ) {
	if ( ! empty( $_POST['ti_gift_note'] ) ) {
		$cart_item_data['ti_gift_note'] = sanitize_text_field( wp_unslash( $_POST['ti_gift_note'] ) );
	}

	return $cart_item_data;
}

add_filter( 'woocommerce_get_item_data', 'ti_gift_note_display', 10, 2 );

function ti_gift_note_display( $item_data, $cart_item ) {
	if ( ! empty( $cart_item['ti_gift_note'] ) ) {
		$item_data[] = array(
			'key'   => __( 'Gift note', 'ti' ),
			'value' => $cart_item['ti_gift_note'],
		);
	}

	return $item_data;
}

Two details matter. Adding a key to $cart_item_data makes the cart treat the same product with different notes as different line items, which is what you want for a personalised field and not what you want for a field that merely records a preference. And nothing here reaches the order: carrying the value onto the order line needs woocommerce_checkout_create_order_line_item, otherwise the note is visible in the cart and gone from the confirmation email, which is the usual complaint after this snippet is pasted. A store that needs several such fields per product is describing product add-ons, which handle the order-line step as well.

Where it fires and where it does not

Context Fires? Why
Single product, simple Yes simple.php template
Single product, variable Yes Inside the variation form, after the button
Single product, grouped Yes After the quantity table and button
Single product, external Yes After the outbound button
Shop and category grid No No form — a link built by woocommerce_loop_add_to_cart_link
Block "Add to Cart with Options" Depends The current block delegates to the classic form and fires it; the newer blockified variant does not
Quick view modals Depends Plugin-specific templates

The grid case trips people most. A field that must exist in the loop needs the loop's own hooks, and because the loop button is an AJAX link rather than a submit, the value cannot travel with the request without JavaScript. The honest answer is that per-item input belongs on the single product page, and the grid's job is to get the shopper there — the same reasoning behind a focused product card.

Variable products and timing

On a variable product the button is disabled until a variation is chosen, and WooCommerce enables it in the browser. Output from this hook is rendered once, server side, and does not change when the variation changes. A field that should only appear for some variations needs to listen for the found_variation event and toggle itself; the hook cannot do that for you.

Three companion hooks sit around the button. woocommerce_before_add_to_cart_button fires before the quantity input on simple and variable products — and after the quantity table on grouped ones. woocommerce_before_add_to_cart_quantity and woocommerce_after_add_to_cart_quantity wrap the quantity input itself, so a field that must sit between quantity and button goes on the second of those. No CSS ordering or template override is needed for any of the four positions.

The broken-button failure

Because output lands inside the form, a stray closing </form>, </div> or </p> changes the document structure around the submit button. The symptoms are specific: clicking the button reloads the page without adding anything, or the quantity field submits but the custom field does not, or the variation form stops enabling the button. Validate the printed HTML before suspecting the hook — pasting the page source into a validator shows the unbalanced tag in seconds, and it is the same discipline that resolves most cases of the add-to-cart button not working.

Verify

  • Add the product from the single page and open the cart: the field value should appear under the line item.
  • Add the same product with a different value: it should be a second line item if the key is in $cart_item_data.
  • Add from the shop grid: the value is absent, and that is expected.
  • View page source around the button: the input is inside <form class="cart"> and the form closes after it, not before.

If the field appears but the value never arrives, check the input's name against the key you read in $_POST. A hyphen in one and an underscore in the other is the most common mismatch, and nothing reports it.

Frequently asked

Printing the input is one third of the job. The value arrives in $_POST on submit, but WooCommerce only stores what you add to the cart item through woocommerce_add_cart_item_data, and only displays what you return from woocommerce_get_item_data. Without those two filters the field is submitted and discarded.
The shop grid does not render the cart form. Its add-to-cart link is a plain anchor built by the woocommerce_loop_add_to_cart_link filter, so there is no form and no before or after hook. Only the single product page runs it.
Yes, as a second submit button with its own name, and then redirect to checkout when that name is present in the request by filtering woocommerce_add_to_cart_redirect. A JavaScript-only button that fakes a click breaks the moment a theme uses AJAX add-to-cart on single pages.

Related guides