Rendering Add to Cart Buttons in Drupal Custom tpls: A How-to Guide

Rendering Add to Cart buttons causes a lot of confusion, it seems. I recently needed to do it on a ctools plugin content type and I wanted to share my experience (and victory) with the community:

WRONG approach - I was attempting to use drupal_get_form() to create the add-to-cart button like this in my template_preprocess function:

= drupal_get_form('MYMODULE_add_to_cart');
//Set variable for my tpl
$vars['cart_form'] = $cart_form;

While this DID create a form that I could render in the tpl with

<?php print render($cart_form); ?>

the problem was that in my content type, I had MULTIPLE products. This meant that even though I managed to pass a Product ID through the form I created via the submit function, the value that was always used by the time I got to using the commerce_add_to_cart_by_id() function always belonged to the first product in the list. ARG! NOW WHAT???

RIGHT approach -

After stomping around and shaking my fists, it hit me...why not just render the button like I did on some product display content type nodes? THIS WAS CLOSE. The button would not just render in my plugin tpl like it does in a node tpl. Hmmmmmmmm....then I found a small golden nugget in an unrelated post....the key was in VIEW MODES! All I had to do was figure out how to translate my node variables into a $content variable that had a view mode like it does in the node_preprocess functions. Here is how you do it:

function template_preprocess_NAME_OF_MY_TEMPLATE(&$vars) {
//Gather the available vars for the node
$node = &$vars['node'];
//Assign content a view_mode so we can render the add to cart button
$content = (node_view($node, 'full', NULL));
$vars['cart'] = $content['field_product'];  //Name of product reference field

Notice that I have used the FULL view mode that correlates to the option in the 'Manage Display' tab on my Content Type that displays my products. As long as the product reference field on that view mode is set to use the 'Add to Cart form' format, the field can be rendered in the custom tpl like this:

print render($cart);

UPDATE as of 07/20/2014 with another scenario:

This time I am making a ctools content type, also with multiple product skus on it, but in this case, I do not have a separate node that references each sku. So because of that, I can't pull in the View Mode to help me with the rendering of the Add to Cart button. Here is what I did:

In my template_preprocess function, I called a custom function to set the cart variable and sent it the product id of the product sku I was working with:

<?php $vars['cart'] = MYMODULE_add_to_cart($pid); ?>

That function looks like this:

function product_sku_pages_add_to_cart($pid) {
$product_data = commerce_product_load($pid);
$line_item = commerce_product_line_item_new($product_data, 1,0, array('context' => array('display_path' => NULL)), 'product');
$wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
// Do not allow the Add to Cart form to combine line items.
$line_item->data['context']['add_to_cart_combine'] = FALSE;
$line_item->data['context']['product_ids'] = str_split($pid);
$cart_form = drupal_get_form('commerce_cart_add_to_cart_form_'.$pid, $line_item);

This was taken in large part from a post that Ryan made in 2013:

Now, because of the multiple skus on the page, I had to alter the drupal_get_form() so that each of the form ids were different. Otherwise, the first item is always added to the cart no matter which form you click on. However, they all need to use the same callback, so I added in this little gem to take care of that part:

I found this on drupal.stackexchange.com

function MYMODULE_forms($form_id) {
  if (
preg_match('/^commerce_cart_add_to_cart_form_\d+$/', $form_id)) {
    return array(
$form_id => array(
'callback' => 'commerce_cart_add_to_cart_form',

I hope this helps a few fellow Drupalers out!

Posted: Mar 13, 2014


joshmiller Josh Miller on April 7, 2014


Thanks for sharing! Really great that you were able to render out an add-to-cart button like this :)