Master the fundamentals of Mustache templating for Quickbutik themes
Mustache is a logic-less templating language that Quickbutik uses to create dynamic storefronts. This guide covers the core syntax and fundamental concepts you need to get started with Mustache templates.
Why Mustache?
Simple, readable syntax
Logic-less design prevents complex business logic in templates
By default, Mustache escapes HTML characters for security. Use {{{variable}}} or {{&variable}} to render unescaped HTML:
Copy
<!-- Escaped HTML (safe) --><div>{{product.description}}</div><!-- Unescaped HTML (renders HTML tags) --><div>{{&product.description}}</div><div>{{{product.description}}}</div>
Security Note: Only use unescaped output ({{&}} or {{{}}}) with trusted content like product descriptions that you control. Never use it with user-generated content.
Sections are the core of Mustache’s logic. They render content based on the truthiness of a value:
Copy
<!-- Show content if product is on sale -->{{#product.has_before_price}} <div class="sale-badge">SALE!</div> <span class="original-price">{{product.before_price}}</span>{{/product.has_before_price}}<!-- Show content if product is NOT sold out -->{{^product.soldOut}} <button class="add-to-cart">Add to Cart</button>{{/product.soldOut}}<!-- Show content if product IS sold out -->{{#product.soldOut}} <div class="sold-out-notice">Sorry, this item is sold out</div>{{/product.soldOut}}
Inside a section, the context changes to that object:
Copy
<!-- Outside any section, we're in global context --><h1>Welcome to {{shop.name}}</h1>{{#basket.items}} <!-- Inside this section, context is each basket item --> <div class="cart-item"> <h3>{{item.title}}</h3> <!-- item.title, not basket.items.item.title --> <p>Quantity: {{qty}}</p> <!-- qty, not basket.items.qty --> <p>Price: {{item.price}}</p> </div>{{/basket.items}}{{#product}} <!-- Inside this section, context is the product object --> <h1>{{title}}</h1> <!-- product.title is now just title --> <p>{{description}}</p> <!-- product.description is now just description -->{{/product}}
<!-- Wrong: Assuming global context inside a loop -->{{#basket.items}} <p>{{product.title}}</p> <!-- product is not available here -->{{/basket.items}}<!-- Correct: Using the actual context -->{{#basket.items}} <p>{{item.title}}</p> <!-- item is the correct context -->{{/basket.items}}
Using unescaped output unnecessarily
Copy
<!-- Wrong: Unescaping when not needed --><h1>{{{product.title}}}</h1><!-- Correct: Default escaping is usually fine --><h1>{{product.title}}</h1><!-- Only unescaped when rendering HTML content --><div>{{&product.description}}</div>