Timber + WooCommerce Ch. 1 – Shop and Single Page

WooCommerce is a pain to integrate with Timber. This tutorial will show you the simplest and quickest way to get started.

Timber + WooCommerce is our series of integrating WooCommerce with Timber Library.

Ch.1 – Shop and Single page
Ch.2 – Cart Dropdown
Ch.3 – Login Page (Coming Soon)

When integrating WooCommerce, it is best to use built-in functions and actions.

The main reason is to keep compatibility with other plugins. Also, it’s tough and confusing to customize.

With that in mind, let’s dive into how to integrate it with Timber:

Note: WooCommerce version used in this tutorial is 3.9.0

1. Add Theme Support

add_action( 'after_setup_theme', 'my_wc_theme_supports' );
function my_wc_theme_supports() {
   add_theme_support( 'woocommerce', [
    'product_grid' => [ 'default_columns' => 4 ],
    'single_image_width' => 480,
  ] );
  add_theme_support( 'wc-product-gallery-zoom' );
  add_theme_support( 'wc-product-gallery-lightbox' );
  add_theme_support( 'wc-product-gallery-slider' );
 * Assign global $product object in Timber
function timber_set_product( $post ) {
  global $product;
  $product = isset( $post->product ) ? $post->product : wc_get_product( $post->ID );

We will use the timber_set_product() function later.

2. Create an Entry Point

All WooCommerce pages will go through a file named woocommerce.php. This is where you put your Timber codes.

$context = Timber::get_context();
if( is_singular( 'product' ) ) {
  $context['product'] = Timber::get_post();
  Timber::render( 'shop-single.twig', $context );
// if SHOP or CATEGORY page
else {
  // if CATEGORY page
  if( is_product_category() || is_product_tag() ) {
    $context['term'] = get_queried_object();
  $context['products'] = Timber::get_posts();
  Timber::render( 'shop.twig', $context );

3. Create the Views

The TWIG snippet below does not include the extend and block part. You need to adapt it to your naming.

Below is for the Shop and Category page:

<header class="shop-header">
  {% if term %}
    <h1>{{ term.name }}</h1>
    {{ term.description | wpautop }}
  {% endif %}
  {% do action("woocommerce_before_main_content") %}
<div class="shop-wrapper">
  {% do action("woocommerce_before_shop_loop") %}
  {% include "_shop-products.twig" %}
  {% do action("woocommerce_after_shop_loop") %}
{% do action("woocommerce_after_main_content") %}

Below is for the Product List partial that we called in [Line 12] above:

<div class="shop-products">
{% for p in products %}
  {{ fn("timber_set_product", p) }}
  <div class="product-tease">
    <a href="{{ p.link }}"> 
        {% do action( 'woocommerce_before_shop_loop_item_title' ) %}
      <h3> {{ p.title }} </h3>
    {% do action("woocommerce_after_shop_loop_item_title") %}
    {% do action("woocommerce_after_shop_loop_item") %}
{% endfor %}

[Line 3] We call the function we created in Step 1. This is to set the global context so the action will show the correct information.

Lastly, we need the view for Single Product page:

{{ fn('the_post') }}  
<div class="product-wrapper">
  {% do action("woocommerce_before_single_product") %}
  <figure class="product-photo">
    {% do action("woocommerce_before_single_product_summary") %}
  <article class="product-summary">
    {% do action("woocommerce_single_product_summary") %}
  {% do action("woocommerce_after_single_product_summary") %}
  {% do action("woocommerce_after_single_product") %}

Done! For other pages like Cart, Checkout, and My Account page, they will go through the standard page.php file. I prefer leaving them untouched.


This tutorial is a simplified version of the official integration guide. Check it out to learn more.

You might ask how do you know about all the action names? Simply open the WooCommerce plugin and check out the codes in /templates folder.

In Part 2, we will learn about creating Cart Popup and Sign In links.

Let us know in the comment below if you have trouble with this integration 🙂

Default image
Henner Setyono
A web developer who mainly build custom theme for WordPress since 2013. Young enough to never had to deal with using Table as layout.
Leave a Reply


  1. Hi Henner,

    Thanks for the informative article! Really looking forward to your next articles on Timber + WooCommerce integration. Was looking for a good tutorial about this since 2016.

  2. Great stuff! Do you have a boilerplate wp theme for this? Thanks in advance!

    • Hi Colin,


      I have a theme boilerplate at https://github.com/hrsetyono/edje-wp-theme. But I haven't updated the WooCommerce part for long (It's been a while since my last WooCommerce project XD)

      It uses the same approach as this tutorial but I was experimenting with removing all actions and use each functions explicitly. It ended up being not a good idea because it breaks plugin compatibility.

  3. Simon Siegenthaler
    Simon Siegenthaler

    Hi and nice article. I am currently trying to integrate a custom checkout page. What I don't find true is this statement by you: "All WooCommerce pages will go through a file named woocommerce.php." When I echo something for product-category, or a single product inside woocommerce.php it works. But it doesn't work for /checkout. This page doesn't get handled by woocommerce.php. Do you have any suggestions?

    • Hi Simon,

      Yes sorry for the mistake. Checkout and Cart page is handled by page.php. If you need to customize Checkout, I recommend to use the built-in Action or Filter instead. It's too complicated to do with Timber and may cause incompatibility with plugins.

      You can see the list of actions by opening the Woocommerce plugin and open /templates folder.

      • Simon Siegenthaler
        Simon Siegenthaler

        You can also overwrite checkout the same way as it is always done. Create a file in this directory:


        As mentioned by you, you shouldn't use twig for that. Just copy the existing file from woocommerce and work in php. This solution is not really satisfying but it works.