Skip to content

Components

INFO

Automatically generated from PHP attributes by the shop:template:documentation command. CSS must not rely on the generated HTML output of components - it may change in future versions.

i:gdpr-form

GDPR consent checkbox/text inserted into parent forms

AttributeTypeRequiredDefaultDescription
codesstringnoGDPR form codes to display (comma-separated)
contextcart-sign-up | footer | sidebarnoOverride auto-detected context
listbooleannoRender as <li> elements without wrapper div

INFO

Self-closing tag, must have no children. Context is auto-detected from the parent form component (newsletter-signup, user-signup, contact form). Use the codes attribute only when you need specific GDPR forms regardless of context.

Auto-detected context (inside newsletter-signup, user-signup, or contact form)

twig
<i:gdpr-form />

Explicit GDPR form codes

twig
<i:gdpr-form codes="newsletter_consent,marketing" />

Generated output:

html
<div>gdprForm({"codes":["newsletter_consent","marketing"]})</div>

i:newsletter-signup

Newsletter subscription form

AttributeTypeRequiredDefaultDescription
redirectstringnoRedirect URL after successful signup
autohidebooleannoHide form for already subscribed visitors
ignore-duplicatedbooleannoSilently ignore duplicate subscriptions
confirm-templatestringnoEmail template ID for confirmation (numeric, requires discount)
discountstringnoDiscount set ID(s) for coupon code (comma-separated, requires confirm-template)
RoleTagRequiredDescription
emailinputyesEmail address input
first_nameinputnoFirst name
last_nameinputnoLast name
streetinputnoStreet address
cityinputnoCity
zipinputnoPostal code
countryinputnoCountry code (cz, sk, ...)
sourceinputselectno
genderinputnoGender radio buttons (value: m or f)
submitbuttonyesSubmit button

INFO

When using discount attribute, the confirm-template attribute is also required. The email template can use {{discountCode}} for a single code or discountCodes array keyed by set ID for multiple codes.

Minimal newsletter form with GDPR consent

twig
<i:newsletter-signup>
<input data-role="email" data-label="Your email">
<i:gdpr-form />
<button data-role="submit">Subscribe</button>
</i:newsletter-signup>

Generated output:

html
<form method="POST" action="/w/~Prijemce_Pridat/save/?uri=%2F" data-component="newsletter-signup">
<label for="email_uid">trans:Your email</label><input id="email_uid" name="email" type="email" required="required">
<div>gdprForm({"context":"newsletter-signup"})</div>
<div class="global-newsletter-url"><label for="newsletter_uid">trans:URL</label><input type="url" id="newsletter_uid" name="url"></div>
<button type="submit">trans:Subscribe</button>
</form>

Newsletter form with extra fields and redirect

twig
<i:newsletter-signup redirect="/thank-you">
<input data-role="email" data-label="Your email">
<input data-role="first_name" data-label="First name">
<i:gdpr-form />
<button data-role="submit">Subscribe</button>
</i:newsletter-signup>

Generated output:

html
<form method="POST" action="/w/~Prijemce_Pridat/save/?uri=%2F" data-component="newsletter-signup">
<label for="email_uid">trans:Your email</label><input id="email_uid" name="email" type="email" required="required">
<label for="first_name_uid">trans:First name</label><input id="first_name_uid" name="first_name" type="text">
<div>gdprForm({"context":"newsletter-signup"})</div>
<div class="global-newsletter-url"><label for="newsletter_uid">trans:URL</label><input type="url" id="newsletter_uid" name="url"></div>
<button type="submit">trans:Subscribe</button>
<input type="hidden" name="redirect" value="/thank-you"></form>

i:search-form

Product search form with autocomplete support

AttributeTypeRequiredDefaultDescription
autocompletebooleannoEnable search autocomplete
min-lengthstringno3Minimum characters before autocomplete triggers
templatestringnoTemplate name for search results (alphanumeric/underscore)
img-formatstringnoImage format for autocomplete results (e.g. "60x60")
RoleTagRequiredDescription
searchinputyesSearch text input
submitbuttonyesSubmit button

INFO

Autocomplete results template is in ajax/search.tpl (or custom file via template attribute). The template receives variable q with the search query. Each result link must have tabindex and data-result-type (product, category, brand, more) attributes. Style the search-current-item CSS class for keyboard navigation highlight.

INFO

Fires autocomplete:loaded event on the form element after each autocomplete results load.

Search form with autocomplete

twig
<i:search-form autocomplete>
<input data-role="search" data-label="Search">
<button data-role="submit">Search</button>
</i:search-form>

Generated output:

html
<form data-min-length="3" action="Vypis/Vyhledavani" method="GET" data-component="product-search" data-autocomplete>
<label for="search_uid">trans:Search</label><input id="search_uid" type="search" name="fraze" required="required" minlength="2" autocomplete="off">
<button type="submit">trans:Search</button>
</form>

i:comment-form

Comment/review form for products, articles, and categories with proof-of-work spam protection

AttributeTypeRequiredDefaultDescription
objectexpressionyesVariable with the commentable object (product, article, or category)
RoleTagRequiredDescription
texttextareanoComment text
titleinputnoComment headline
ratingselectnoStar rating 1-5 (products only, options auto-generated)
authorinputnoAuthor name
emailinputnoAuthor email
phoneinputnoAuthor phone
parentinputnoParent comment ID for threaded replies (auto-set as hidden)
submitbuttonyesSubmit button

INFO

Spam protection uses proof-of-work (no CAPTCHA needed). The form submits via AJAX automatically. Rating select options (stars) are auto-populated. For threaded replies, set parent slot value to the parent comment ID.

Basic product comment

twig
<i:comment-form object="product">
<input data-role="author" data-label="Name">
<textarea data-role="text" data-label="Comment"></textarea>
<button data-role="submit">Submit</button>
</i:comment-form>

Generated output:

html
<form method="POST" action="/api/comments" data-component="comment-form">
<label for="author_uid">trans:Name</label><input id="author_uid" name="author" type="text" autocomplete="name">
<label for="text_uid">trans:Comment</label><textarea id="text_uid" name="text"></textarea>
<button type="submit">trans:Submit</button>
<input name="commentType" type="hidden" value="zbozi">
<input name="objectId" type="hidden" value="123">
</form>

Product review with all fields

twig
<i:comment-form object="product">
<input data-role="author" data-label="Name">
<input data-role="email" data-label="Email">
<input data-role="title" data-label="Headline">
<textarea data-role="text" data-label="Review"></textarea>
<button data-role="submit">Submit review</button>
</i:comment-form>

Generated output:

html
<form method="POST" action="/api/comments" data-component="comment-form">
<label for="author_uid">trans:Name</label><input id="author_uid" name="author" type="text" autocomplete="name">
<label for="email_uid">trans:Email</label><input id="email_uid" name="email" type="email" autocomplete="email">
<label for="title_uid">trans:Headline</label><input id="title_uid" name="title" type="text">
<label for="text_uid">trans:Review</label><textarea id="text_uid" name="text"></textarea>
<button type="submit">trans:Submit review</button>
<input name="commentType" type="hidden" value="zbozi">
<input name="objectId" type="hidden" value="456">
</form>

i:list-empty

Defines the empty-state template macro shown when listing has no results

AttributeTypeRequiredDefaultDescription
typeproduct | article | actualityyesListing type
namestringnodefaultEmpty template macro name

Empty state with separate messages for filtered and default states

twig
<i:list-item type="product">{{product.name}}</i:list-item><i:list-empty type="product"><p data-role="default">No products found.</p><p data-role="filter">No products match your filter.</p></i:list-empty><i:list-container type="product"><div data-role="empty"></div><div data-role="list"></div></i:list-container>

Generated output:

html
<div data-type="product" data-list-config="eyJjb250ZW50R3JvdXBJZCI6bnVsbCwiY3VycmVudFBhZ2UiOm51bGwsInBlclBhZ2UiOm51bGwsInNvcnQiOm51bGx9" data-component="list-container"><div data-role="empty"><p>trans:No products found.</p></div><div data-role="list"></div></div><script>{inline-listing-loaded};</script>

i:list-item

Defines the template macro for a single item in a listing

AttributeTypeRequiredDefaultDescription
typeproduct | article | actualityyesType of item
tagstringnodivHTML tag to wrap each item
namestringnodefaultMacro name (must match list-container name)

Basic product item template

twig
<i:list-item type="product"><h3>{{product.name}}</h3></i:list-item>{{_self.item_product_default(product)}}

Generated output:

html
<div data-track="123|0||" data-oid="p123"><h3>name</h3></div>

i:list-container

Main listing container that fetches items from a repository and renders them with pagination

AttributeTypeRequiredDefaultDescription
typeproduct | article | actualityyesType of items to display
sourceexpressionnoCustom repository query parameters as JSON object (e.g. {category: kategorie.id, limit: 8})
collectionexpressionnoCustom Twig collection to iterate over instead of repository
namestringnodefaultTemplate macro name for the item box
wrapstringnoWrap items in groups (e.g. "3:div.row" wraps every 3 items in a div.row)
appendstringnoInsert elements at positions (e.g. "3:div.spacer")
insert-beforestringnoInsert macro name to call before each item
insert-afterstringnoInsert macro name to call after each item
hide-emptybooleannoHide the entire container when no items found (instead of showing empty slot)
scroll-offsetstringnoOffset for scroll-to behavior
analytics-namestringnoAnalytics identifier for tracking
RoleTagRequiredDescription
listdivyesContainer for rendered items (must be empty)
emptynoShown when no items found (required unless hide-empty)
paginationdivnoPagination controls (can have multiple)

Default product listing

twig
<i:list-item type="product">{{product.name}}</i:list-item><i:list-empty type="product">No products found</i:list-empty><i:list-container type="product"><div data-role="list"></div><div data-role="empty"></div></i:list-container>

Generated output:

html
<div data-type="product" data-list-config="eyJjb250ZW50R3JvdXBJZCI6bnVsbCwiY3VycmVudFBhZ2UiOm51bGwsInBlclBhZ2UiOm51bGwsInNvcnQiOm51bGx9" data-component="list-container"><div data-role="list"><div data-track="123|0|1|" data-oid="p123">abc</div></div><div data-role="empty"></div></div><script>{inline-listing-loaded};</script>

Custom product listing with category filter

twig
<i:list-container type="product" source="{category: kategorie.id, limit: 4}">
<div data-role="list"></div>
<div data-role="empty">No products</div>
</i:list-container>

i:list-pagination

Defines the pagination template macro for listings

AttributeTypeRequiredDefaultDescription
typeproduct | article | actualityyesListing type
namestringnodefaultPagination macro name
RoleTagRequiredDescription
add-pageanoLoad-more button, shown when next page exists

Simple load-more pagination

twig
<i:list-pagination type="product">
<a data-role="add-page">Load more</a>
</i:list-pagination>

i:list-dynamic

Dynamic content area within a listing that renders content based on type (headline, text, subcategories)

AttributeTypeRequiredDefaultDescription
typeheadline | text | subcategories | otheryesType of dynamic content
variablesstringyesTwig variables to pass to the dynamic content

Category headline in listing

twig
<div i:list-dynamic type="headline" variables="kategorie">{{ kategorie.name }}</div>

i:banners

Banner/slider component with responsive images, CTA buttons, and carousel support

AttributeTypeRequiredDefaultDescription
sourceexpressionyesTwig expression returning banner collection
classstringnoCSS class for the banner wrapper
pluginstringnoSlider plugin name (e.g. "swiper")
clickcta | banner | off | autonoautoClick behavior - CTA button, whole banner, disabled, or auto
visible-slidesstringno1Number of visible slides
prioritystringnohighLoading priority (high = no lazy load for visible slides)
wrapstringnoWrapper element (e.g. "div.banner-inner")
arrowsbooleannoShow navigation arrows
dotsbooleannoShow pagination dots
thumbnailsbooleannoShow thumbnail navigation
progressbooleannoShow progress bar
transparentbooleannoPreserve image transparency
fillbooleannoUse object-fit: cover on images

Responsive hero banner with Swiper plugin

twig
<i:banners source="banners" class="hero-banner" plugin="swiper" arrows dots><source format="1200x400" src="desktop"/><source format="400x300" src="mobile"/></i:banners>

Generated output:

html
<div class="banner hero-banner swiper-container">
<div class="banner-wrap swiper-wrapper">
<div class="banner-item banner--item__1 banner--item__first banner--item__last swiper-slide">
<picture>
<source srcset="https://i00.eu/img/999999/400x300/b70ltmq7/17.webp 1x,https://i00.eu/img/999999/800x600c/14pro84s/17.webp 2x" media="(max-width: 400px)">
<img width="730" height="400" srcset="https://i00.eu/img/999999/1200x400/eb9g3pmd/17.webp 1x,https://i00.eu/img/999999/2400x800c/aergzgi6/17.webp 2x" src="https://i00.eu/img/999999/1200x400/eb9g3pmd/17.webp">
</picture>
</div>
</div>
<div class="pagination swiper-pagination"></div>
<div class="button-next swiper-button-next"></div>
<div class="button-prev swiper-button-prev"></div>
</div>

i:menu

Defines a menu macro in _menu.tpl, rendered by i:menu-container

AttributeTypeRequiredDefaultDescription
namestringyesMenu macro name (must match i:menu-container name)

INFO

Must be defined in _menu.tpl file. The lazy variable is automatically passed by i:menu-container - use it to skip rendering deep submenus that will be loaded via AJAX. Menu output is cached, so use active_menu_placeholder_class('className', category) for active state instead of Twig conditionals. Also accepts a route hash like active_menu_placeholder_class('active', 'Vypis/Slevy') for page type matching.

Two-level category menu with active class and lazy support

twig
<i:menu name="main">
<ul>
{% for category in repository.category.findBy({'parent': 0}) %}
<li class="{{ active_menu_placeholder_class('active', category) }}">
<a href="{{ category.url }}">{{ category.name }}</a>
{% if not lazy and category.subcategories is not empty %}
<ul>
{% for sub in category.subcategories %}
<li class="{{ active_menu_placeholder_class('active', sub) }}">
<a href="{{ sub.url }}">{{ sub.name }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</i:menu>

i:menu-container

Category menu container that loads menu from _menu.tpl template with optional lazy loading

AttributeTypeRequiredDefaultDescription
namestringyesMenu macro name defined in _menu.tpl
tagstringnodivHTML wrapper tag
lazybooleannoLoad menu asynchronously via AJAX

INFO

Must be an empty element (no children). With lazy, the non-lazy part renders server-side and the full menu loads via AJAX, significantly improving page speed. Fires menu:load event on the container element after async load completes.

Lazy-loaded main navigation menu

twig
<div><i:menu-container name="main" tag="div" lazy /></div>

Generated output:

html
<div><div id="js-menu-uid">trans:hello</div><script>(function(){var x=new XMLHttpRequest();x.open("GET","/ajax-menu-url");x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.send();x.onreadystatechange=function(){if(4===x.readyState){var t=x.responseText;var l=JSON.parse(document.body.dataset["activeCategories"]),r=document.body.dataset["routeHash"];for(var i=0;i<l.length;i++)t=t.replace(new RegExp("([a-zA-Z0-9\-_]+)\-menu\-class\-placeholder\-"+l[i]+"([^0-9])","g"),"$1$2");t=t.replace(new RegExp("([a-zA-Z0-9\-_]+)\-menu\-class\-placeholder\-"+r+"([^0-9a-z])","g"),"$1$2");document.getElementById("js-menu-uid").innerHTML=t.replace(new RegExp("[a-zA-Z0-9\-_]+\-menu\-class\-placeholder\-[0-9]+","g"),"");const event = document.createEvent("CustomEvent");event.initCustomEvent("menu:load", true, true, {});document.getElementById("js-menu-uid").dispatchEvent(event);}};})()</script></div>

Sidebar menu rendered as list

twig
<div i:menu-container name="sidebar" tag="ul"></div>

i:delivery-transports

Container for transport/shipping options in the checkout process

AttributeTypeRequiredDefaultDescription
tagstringnodivHTML wrapper tag
reload-targetsstringnoCSS selectors of elements to reload when transport changes

Transport selection in checkout

twig
<i:delivery-transports>{% for transport in cart.availableTransportMethods %}<i:delivery-transport transport="transport" tag="div"><input name="transport" type="radio" />{{ transport.name }}<i:delivery-branch button-text="Choose branch" /></i:delivery-transport>{% endfor %}</i:delivery-transports>

Generated output:

html
<div data-component="delivery-transports" data-dynamic-content="7eNRw6uOGJUoTV9lG4yoJ7tzbjZVbB5agwTz0Ph+6ks="><div data-component="delivery-transport"><input name="transport" type="radio" value="7">PPL<div data-component="delivery-branch"><input type="hidden" name="pobocka[zasilkovna_stat_1000]" value="5" data-config='{"type":"ppl"}' data-branch='{"code":5,"label":"Drtinova 10"}' data-excluded-places="[]"><span>Drtinova 10</span> <a href="#">trans:Choose branch</a></div></div></div>

i:delivery-transport

Single transport option within delivery-transports

AttributeTypeRequiredDefaultDescription
transportexpressionyesTwig expression for the transport object from cart.transports loop
tagstringyesHTML wrapper tag

Transport option with branch selection

twig
<i:delivery-transport transport="transport" tag="label"><input name="transport" type="radio" />{{ transport.name }}<i:delivery-branch button-text="Choose pickup point" /></i:delivery-transport>

Generated output:

html
<label data-component="delivery-transport"><input name="transport" type="radio" value="3">PPL<div data-component="delivery-branch"><input type="hidden" name="pobocka[ppl_cz]" data-config='{"type":"ppl"}' data-excluded-places="[]"><span></span> <a href="#">trans:Choose pickup point</a></div></label>

i:delivery-payments

Container for payment method options in the checkout process

AttributeTypeRequiredDefaultDescription
tagstringnodivHTML wrapper tag
reload-targetsstringnoCSS selectors of elements to reload when payment changes

Payment method selection in checkout

twig
<i:delivery-payments>{% for payment in cart.availablePaymentMethods %}<i:delivery-payment payment="payment" tag="label"><input name="payment" type="radio" />{{ payment.name }}</i:delivery-payment>{% endfor %}</i:delivery-payments>

Generated output:

html
<div data-component="delivery-payments" data-dynamic-content="7eNRw6uOGJUoTV9lG4yoJ7tzbjZVbB5agwTz0Ph+6ks="><label data-component="delivery-payment"><input name="payment" type="radio" value="7">Credit card</label></div>

i:delivery-payment

Single payment method option within delivery-payments

AttributeTypeRequiredDefaultDescription
paymentexpressionyesTwig expression for the payment object from cart.payments loop
tagstringyesHTML wrapper tag

Payment option with radio button

twig
<i:delivery-payment payment="payment" tag="label"><input name="payment" type="radio" />{{ payment.name }}</i:delivery-payment>

Generated output:

html
<label data-component="delivery-payment"><input name="payment" type="radio" value="5">Credit card</label>

i:delivery-branch

Pickup point/branch selector within a delivery-transport (renders branch select widget)

AttributeTypeRequiredDefaultDescription
transportexpressionyesTransport object reference (auto-set by parent delivery-transport)
button-textstringyesText for the branch selection button (translatable)

Branch selector (must be inside i:delivery-transport)

twig
<i:delivery-branch button-text="Choose pickup point" />

i:filter-form

Defines a filter form template macro for product/article filtering

AttributeTypeRequiredDefaultDescription
namestringyesFilter form macro name (referenced by filter-placeholder)
auto-scrollstringnoScroll to listing after filter change
RoleTagRequiredDescription
resetanoFilter reset link/button (hidden when no filter is active)

Basic filter form with reset button

twig
<i:filter-form name="default">
<a data-role="reset">Reset filters</a>
{% for attr in attributes %}
<i:filter-attribute attribute="attr" />
{% endfor %}
</i:filter-form>

i:filter-placeholder

Renders a filter-form macro at a specific location with given configuration

AttributeTypeRequiredDefaultDescription
namestringyesFilter form macro name (must match a filter-form name)
configstringyesFilter configuration code
hide-emptybooleannoHide the filter form when no filter attributes are available
always-collapsedbooleannoRender all filter groups initially collapsed

Render default filter form, hidden when empty

twig
<i:filter-placeholder name="default" config="default" hide-empty />

i:variant-choice

Product variant selector with step-by-step attribute selection

AttributeTypeRequiredDefaultDescription
data-productexpressionyesProduct ID expression
reload-targetsstringnoCSS selectors of elements to reload when variant changes
RoleTagRequiredDescription
stepyesContainer for each variant selection step

Variant selector with color/size steps

twig
<div i:variant-choice data-product="{{product.id}}" i:dynamic="product" reload-targets=".product-price,.product-availability">
{% for step in product.variantChoice.steps %}
<div data-role="step">
<strong>{{ step.name }}:</strong>
{% for value in step.values %}
<button>{{ value.name }}</button>
{% endfor %}
</div>
{% endfor %}
</div>

i:dynamic

Wraps content for dynamic (AJAX) reloading when server-side state changes

AttributeTypeRequiredDefaultDescription
variablesstringyesComma-separated Twig variables that trigger re-render
lazynone | auto | manualnononeLazy loading mode - none (server-rendered), auto (lazy on page load), manual

Cart summary that updates dynamically

twig
<i:dynamic variables="order">Order #{{ order.id }}</i:dynamic>

Generated output:

html
<div data-dynamic-content="+U8r+mkvbR7EJVHWtAmbvIN9KvgnUinu7XHN9oUfE9Nb6OKzPQzr8vs6btDlfyPu1PqQ3yj1hmwFpfxEwO5Lxg==">Order #7</div>

i:img

Responsive image with automatic srcset, DPR-aware rendering, and lazy loading

AttributeTypeRequiredDefaultDescription
srcexpressionyesTwig expression returning an Image object (e.g. product.image)
formatstringyesDimensions WxH or comma-separated list (e.g. "359x479" or "800x600,400x300")
sizesstringnoHTML sizes attribute (required when multiple formats)
fillbooleannoUse object-fit: cover
vectorbooleannoSVG source - skip bitmap optimization
transparentbooleannoPreserve transparency (PNG/WebP instead of JPEG)
watermarkbooleannoApply watermark overlay
qualitylow | normal | high | losslessnoCompression quality
loadingstringnoLoading strategy (lazy/eager)
lazyloadbooleannoEnable lazy loading

Basic product image

twig
<i:img src="product.img" format="100x100" fill />

Generated output:

html
<img alt="description" width="100" height="100" srcset="http://100x100f.jpg 1x,http://200x200fc.jpg 2x" src="http://100x100f.jpg">

Lazy-loaded lifestyle image variant

twig
<i:img src="product.image('lifestyle')" format="800x600" fill loading="lazy" />

SVG brand logo

twig
<i:img src="brand.image" format="200x100" vector transparent fill />