Skip to main content

Product

Display WooCommerce product data in your templates. Requires WooCommerce to be active.

The Product provider inherits all Post fields — title, link, excerpt, thumbnail, dates, author, custom fields, and everything else. Only product-specific fields are listed here.

note

The Product provider only activates when WooCommerce is active. If WooCommerce is deactivated, product fields return empty values. Post fields (title, link, etc.) still work because products are a custom post type.

How It Works

When you loop over products with post_type: 'product', use product as your variable name. The CPT routing picks it up automatically and makes all product fields available:

{% for product in get_posts({post_type: 'product', posts_per_page: 6}) %}
{{ product.title }}
{{ product.price | currency }}
{% endfor %}

You still have access to every Post field — product.title, product.thumbnail, product.excerpt, product.link, etc.

Product-Specific Fields

Pricing

FieldReturnsDescription
pricestringEffective price (sale price if on sale, regular otherwise)
regular_pricestringRegular price
sale_pricestringSale price (empty if not on sale)
is_on_saleboolWhether the product is currently on sale
discount_percentfloatDiscount percentage
price_htmlstringFull WooCommerce price HTML (includes <del> for sale)
price_suffixstringPrice suffix text (e.g. "excl. tax")
{{ product.price | currency }}
{{ product.regular_price | currency }}
{{ product.sale_price | currency }}
{{ product.discount_percent }}%
{{ product.price_html }}

The currency Filter

Formats a number as WooCommerce currency using wc_price(). Uses your store's configured currency symbol, thousand separator, and decimal settings.

{{ product.price | currency }}            <!-- $29.99 -->
{{ product.regular_price | currency }} <!-- $39.99 -->

Without currency, you get the raw number. With it, you get the formatted output matching your WooCommerce settings.

Inventory

FieldReturnsDescription
skustringProduct SKU
stock_quantityintStock quantity (null if not tracked)
stock_statusstringinstock, outofstock, or onbackorder
is_in_stockboolWhether the product is in stock
{{ product.sku }}
{{ product.stock_quantity }}
{{ product.stock_status }}
{% if product.is_in_stock %}Available{% endif %}

Product Properties

FieldReturnsDescription
is_virtualboolWhether virtual (no shipping)
is_downloadableboolWhether downloadable
is_featuredboolWhether marked as featured
product_typestringsimple, variable, grouped, or external
weightstringProduct weight
lengthstringProduct length
widthstringProduct width
heightstringProduct height
{{ product.product_type }}
{{ product.weight }}
{{ product.length }} × {{ product.width }} × {{ product.height }}
{% if product.is_featured %}{% endif %}
FieldArgumentsReturnsDescription
product_gallerylimitImage[]Gallery images
{% for image in product.product_gallery %}
{{ image.src('medium') }}
{{ image.alt }}
{% endfor %}

{% for image in product.product_gallery(4) %}
<!-- Limited to 4 images -->
{% endfor %}

Each image is a full Image object — src, alt, width, height, and all size variants.

FieldArgumentsReturnsDescription
upsellslimitProduct[]Upsell products
cross_sellslimitCross-sell products
{% for upsell in product.upsells %}
{{ upsell.title }}
{{ upsell.price | currency }}
{% endfor %}

{% for upsell in product.upsells(3) %}
<!-- Limited to 3 upsells -->
{% endfor %}

{% for cross in product.cross_sells(4) %}
{{ cross.title }}
{% endfor %}

Each related product is itself a full Product object — you can access all product fields on it.

Ratings & Reviews

FieldReturnsDescription
average_ratingfloatAverage rating (0–5)
review_countintNumber of reviews
rating_countintNumber of ratings
{{ product.average_rating }} / 5
{{ product.review_count }} reviews

Cart

FieldReturnsDescription
add_to_cart_urlstringAdd to cart URL
add_to_cart_textstringAdd to cart button text
{{ product.add_to_cart_url }}
{{ product.add_to_cart_text }}

Common Patterns

Product card with sale badge

{% for product in get_posts({post_type: 'product', posts_per_page: 8}) %}
{{ product.thumbnail.src('medium') }}
{{ product.title }}

{% if product.is_on_sale %}
{{ product.regular_price | currency }}
{{ product.price | currency }}
-{{ product.discount_percent }}%
{% else %}
{{ product.price | currency }}
{% endif %}

{{ product.add_to_cart_text }}
{% endfor %}

Product grid with stock status

{% for product in get_posts({post_type: 'product', posts_per_page: 12}) %}
{{ product.thumbnail.src('woocommerce_thumbnail') }}
{{ product.title }}
{{ product.price | currency }}

{% if product.is_in_stock %}
{% if product.stock_quantity and product.stock_quantity <= 3 %}
Only {{ product.stock_quantity }} left
{% else %}
In stock
{% endif %}
{% else %}
Out of stock
{% endif %}
{% endfor %}
<!-- Main image -->
{{ product.thumbnail.src('large') }}

<!-- Gallery thumbnails -->
{% for image in product.product_gallery %}
{{ image.src('thumbnail') }}
{{ image.alt }}
{% endfor %}

Upsells section

{% if product.upsells %}
{% for upsell in product.upsells(4) %}
{{ upsell.thumbnail.src('medium') }}
{{ upsell.title }}
{{ upsell.price | currency }}
{{ upsell.average_rating }} / 5
{% endfor %}
{% endif %}
Common mistake

Don't forget the currency filter when displaying prices. Without it, product.price returns a raw number like 29.99 — no currency symbol, no formatting. Always use {{ product.price | currency }} to match your WooCommerce store settings.

Next Steps

  • Post provider — All inherited fields available on products
  • Image provider — Working with gallery images and thumbnails
  • Loop — Query and loop over products with get_posts