Skip to main content

Woo Recipes

Recipes are short examples for one job at a time. For complete page composition, start with Product Archive, Single Product, Cart Page, or Checkout Page.

For complete starter route ownership, read Woo Template Integration Contract before composing individual recipes. It covers the archive-product, single-product, cart, checkout, order confirmation, taxonomy archive, and Woo page option contract that these component recipes assume.

Product Archive Card

Use a product loop and bind each card with {item.*}:

Product archive purchase card
{#loop products as item}
<OmeWooAddToCartForm product='{{"product_id":"{item.id}","product_type":"{item.omewoo.product.type}"}}'>
{#slot default}
<OmeWooAddToCartButton behavior='{{"mode":"counter","trigger":"form_submit"}}' />
{/slot}
</OmeWooAddToCartForm>
{/loop}

Why it works: the form owns product ID/type and form submission; the button is a submit control inside the form. Add CartCount above the loop when the archive should show a live cart count.

Single Product Quantity

Use {this.*} on a single product template and set button quantity bounds:

Single product form with quantity bounds
<OmeWooAddToCartForm product='{{"product_id":"{this.id}","product_type":"{this.omewoo.product.type}"}}'>
{#slot default}
<OmeWooAddToCartButton
behavior='{{"mode":"counter","trigger":"form_submit"}}'
quantity='{{"min":"1","max":"3","default":"2"}}'
/>
{/slot}
</OmeWooAddToCartForm>

Why it works: counter mode renders a quantity input, and the form-submit trigger keeps quantity and product data on the parent form payload.

Variable Product Selector

Loop over product attributes and bind each AttributeSelector to the current attribute object:

Variation attribute selectors inside a purchase form
<OmeWooAddToCartForm product='{{"product_id":"{this.id}","product_type":"{this.omewoo.product.type}","variants":{this.omewoo.variants}}}'>
{#slot default}
{#loop this.omewoo.attributes as attribute}
<OmeWooAttributeSelector
target='{{"attribute":{attribute}}}'
ui='{{"mode":"radio","aria_label":"Choose {attribute.label}"}}'
/>
{/loop}
<OmeWooAddToCartButton behavior='{{"mode":"counter","trigger":"form_submit"}}' />
{/slot}
</OmeWooAddToCartForm>

Why it works: the selector writes selected variation attribute state into the purchase form. The form blocks submit until required variation options are selected, and unavailable options do not select. For products with more than one variation attribute, product.variants lets the runtime resolve the final variation_id from the complete selected combination, such as Color = Blue plus Size = Small.

Color Swatch Selector

Switch only the appropriate attribute to swatches in Etch, then map exact option slugs in scoped CSS:

Color swatch attribute selector
<OmeWooAttributeSelector
target='{{"attribute":{attribute}}}'
ui='{{"mode":"color-swatch","aria_label":"Choose {attribute.label}"}}'
behavior='{{"show_swatch_label":false,"unavailable_options":"disabled"}}'
/>
CSS-first swatch mapping
[data-ome-woo-attribute-selector][data-ome-ui-choice-mode="color-swatch"]
[data-ome-value="forest"] {
--ome-woo-swatch-color: #234f32;
--ome-woo-swatch-fallback-display: none;
}

If forest is not mapped, its text remains visible. Do not hide the fallback globally. See Product Purchase for image mappings, light-color borders, keyboard behavior, and stable Woo variation-image rules.

Cart Rows and Empty State

Place cart item atoms inside the CartItems default slot and empty content inside the empty slot:

Cart item rows with empty fallback
<OmeWooCartItems>
{#slot default}
<OmeWooCartItemImage />
<OmeWooCartItemTitle />
<OmeWooCartItemQuantity />
<OmeWooCartItemRemove />
{/slot}
{#slot empty}
Your cart is empty.
{/slot}
</OmeWooCartItems>

Why it works: CartItems owns the repeated cart row. The item atoms read the current row that CartItems hydrates.

Coupons

Use CouponForm to apply codes, CouponAppliedList to render current coupons, and CouponRemoveButton inside the coupon row:

Coupon apply and remove
<OmeWooCouponForm />

<OmeWooCouponAppliedList>
{#slot default}
<OmeWooCouponRemoveButton />
{/slot}
</OmeWooCouponAppliedList>

Why it works: CouponRemoveButton reads the coupon code from the row generated by CouponAppliedList.

Shipping Methods

Use ShippingMethodSelector wherever cart shipping selection should be available:

Shipping method selector in button-radio mode
<OmeWooShippingMethodSelector
ui='{{"mode":"button-radio","aria_label":"Choose a shipping method"}}'
content='{{"show_price":true,"price_separator":" - "}}'
/>

Why it works: the selector uses package-aware rate values, so Woo can distinguish shipping rates across packages.

Checkout Shell

Use a provider, a form, address forms, selectors, terms, notices, and place order:

Checkout provider and form shell
<OmeWooCheckoutProvider>
{#slot default}
<OmeWooCheckoutForm>
{#slot default}
<OmeWooBillingAddressForm />
<OmeWooShippingAddressForm />
<OmeWooPaymentMethodSelector />
<OmeWooTermsCheckbox />
<OmeWooCheckoutNotices />
<OmeWooPlaceOrderButton />
{/slot}
</OmeWooCheckoutForm>
{/slot}
</OmeWooCheckoutProvider>

Why it works: CheckoutProvider creates the checkout scope, CheckoutForm owns serialization and submission, and PlaceOrderButton submits the parent form.

Native Hook Compatibility

Use the same components as normal. The important point is that Store API requests still run Woo hooks:

  • Add-to-cart validation can block the Etch form and render a Woo notice.
  • Add-to-cart hooks can inject cart item data.
  • Pricing hooks can change totals rendered by cart and order summary components.
  • Checkout hooks can update Store API orders and payment context.

Event Integrations

Use Woo Custom Events for mini-cart drawers, analytics, cart refresh integrations, and checkout instrumentation. Keep event examples there so recipes stay focused on component composition.