Sidebar Template
The sidebar template displays filters in a persistent sidebar next to the product grid, ideal for desktop experiences with extensive filtering options.
Implementation¶
Create a new snippet (typically named rebuy-smart-collection-template) and paste the template code. Then render the snippet in your collection template:
Add this at the bottom of collection.liquid or the main file that renders on collection pages.
Template Detection
When Rebuy loads, it searches for a custom template in the theme code. If found, it uses the custom template instead of the standard template.
Template¶
The template wraps content with {% raw %} and {% endraw %} so Vue.js double curly brace syntax doesn't get parsed as Liquid.
{% raw %}
<script id="rebuy-smart-collection-sidebar-template" type="text/template">
<div v-if="shouldShowCollectionsPage()" id="rebuy-smart-collection-sidebar" class="rebuy-smart-collections">
<div class="rebuy-smart-collections__page">
<!-- Hero Section -->
<div class="rebuy-smart-collections__hero" v-bind:class="{ 'rebuy-smart-collections__hero-no-image': !shouldShowHeroImage() }">
<div class="rebuy-smart-collections__hero-text">
<h1 class="rebuy-smart-collections__hero-title">{{settings.collectionData?.name || ''}}</h1>
<div v-if="shouldShowHeroDescription()" class="rebuy-smart-collections__hero-description" v-html="settings.collectionData?.description || ''"></div>
</div>
<div v-if="shouldShowHeroImage()" class="rebuy-smart-collections__hero-image">
<img v-bind:src="getCollectionHeroImage()" />
</div>
</div>
<!-- Main Layout -->
<div class="rebuy-smart-collections__layout">
<!-- Sidebar Filters -->
<aside class="rebuy-smart-collections__sidebar">
<div class="rebuy-smart-collections__sidebar-header">
<h3>Filters</h3>
<button v-if="hasSelectedFilters()" @click="resetFilters()" class="rebuy-smart-collections__reset-link">
Reset All
</button>
</div>
<!-- Price Range -->
<div v-if="shouldShowPriceFilter()" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">Price</h4>
<div class="rebuy-smart-collections__price-inputs">
<div class="rebuy-smart-collections__price-field">
<label for="price-min" class="sr-only">Minimum price</label>
<span class="rebuy-smart-collections__currency" v-html="shopCurrencySymbol()"></span>
<input type="number" id="price-min" v-model="filterPrice.min" placeholder="Min" />
</div>
<span class="rebuy-smart-collections__price-separator">-</span>
<div class="rebuy-smart-collections__price-field">
<label for="price-max" class="sr-only">Maximum price</label>
<span class="rebuy-smart-collections__currency" v-html="shopCurrencySymbol()"></span>
<input type="number" id="price-max" v-model="filterPrice.max" placeholder="Max" />
</div>
<button @click="applyPriceFilter()" class="rebuy-smart-collections__price-apply">Apply</button>
</div>
</div>
<!-- Availability -->
<div v-if="shouldShowAvailabilityFilter()" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">Availability</h4>
<div class="rebuy-smart-collections__filter-options">
<label class="rebuy-smart-collections__filter-option">
<input type="radio" v-model="filterAvailability" value="all" @change="applyFilters()" />
<span>All</span>
</label>
<label class="rebuy-smart-collections__filter-option">
<input type="radio" v-model="filterAvailability" value="in-stock" @change="applyFilters()" />
<span>In Stock</span>
</label>
<label class="rebuy-smart-collections__filter-option">
<input type="radio" v-model="filterAvailability" value="sold-out" @change="applyFilters()" />
<span>Sold Out</span>
</label>
</div>
</div>
<!-- Tags -->
<div v-if="aggregatedFilters.tags && aggregatedFilters.tags.length > 0" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">Tags</h4>
<div class="rebuy-smart-collections__filter-options">
<label v-for="tag in aggregatedFilters.tags" class="rebuy-smart-collections__filter-option">
<input type="checkbox" :value="tag" v-model="selectedTags" @change="applyFilters()" />
<span>{{tag}}</span>
</label>
</div>
</div>
<!-- Vendors -->
<div v-if="aggregatedFilters.vendors && aggregatedFilters.vendors.length > 0" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">Vendor</h4>
<div class="rebuy-smart-collections__filter-options">
<label v-for="vendor in aggregatedFilters.vendors" class="rebuy-smart-collections__filter-option">
<input type="checkbox" :value="vendor" v-model="selectedVendors" @change="applyFilters()" />
<span>{{vendor}}</span>
</label>
</div>
</div>
<!-- Product Types -->
<div v-if="aggregatedFilters.productTypes && aggregatedFilters.productTypes.length > 0" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">Product Type</h4>
<div class="rebuy-smart-collections__filter-options">
<label v-for="type in aggregatedFilters.productTypes" class="rebuy-smart-collections__filter-option">
<input type="checkbox" :value="type" v-model="selectedTypes" @change="applyFilters()" />
<span>{{type}}</span>
</label>
</div>
</div>
<!-- Product Options -->
<div v-for="(values, optionName) in aggregatedFilters.options" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">{{optionName}}</h4>
<div class="rebuy-smart-collections__filter-options">
<label v-for="value in values" class="rebuy-smart-collections__filter-option">
<input type="checkbox" :value="value" v-model="selectedOptions[optionName]" @change="applyFilters()" />
<span>{{value}}</span>
</label>
</div>
</div>
<!-- Metafields -->
<div v-for="(values, metafieldKey) in aggregatedFilters.metafields" class="rebuy-smart-collections__filter-section">
<h4 class="rebuy-smart-collections__filter-title">{{formatMetafieldTitle(metafieldKey)}}</h4>
<div class="rebuy-smart-collections__filter-options">
<label v-for="value in values" class="rebuy-smart-collections__filter-option">
<input type="checkbox" :value="value" v-model="selectedMetafields[metafieldKey]" @change="applyFilters()" />
<span>{{value}}</span>
</label>
</div>
</div>
</aside>
<!-- Products Area -->
<main class="rebuy-smart-collections__main">
<!-- Mobile Filter Trigger -->
<div class="rebuy-smart-collections__mobile-controls">
<button @click="openMobileFilters()" class="rebuy-smart-collections__mobile-filter-btn">
<rebuy-icon name="filter"></rebuy-icon>
Filters
<span v-if="activeFilterCount > 0" class="rebuy-smart-collections__filter-count">{{activeFilterCount}}</span>
</button>
<div class="rebuy-smart-collections__mobile-sort">
<label for="mobile-sort" class="sr-only">Sort by</label>
<select id="mobile-sort" v-model="sortBy" @change="handleSort()">
<option v-for="option in sortOptions" :value="option.value">{{option.label}}</option>
</select>
</div>
</div>
<!-- Desktop Sort -->
<div class="rebuy-smart-collections__desktop-controls">
<div class="rebuy-smart-collections__results-count">
{{totalProducts}} products
</div>
<div class="rebuy-smart-collections__sort">
<label for="desktop-sort">Sort by:</label>
<select id="desktop-sort" v-model="sortBy" @change="handleSort()">
<option v-for="option in sortOptions" :value="option.value">{{option.label}}</option>
</select>
</div>
</div>
<!-- Selected Filters -->
<div v-if="hasSelectedFilters()" class="rebuy-smart-collections__selected-filters">
<span v-for="filter in getSelectedFiltersDisplay()" class="rebuy-smart-collections__selected-filter">
{{filter.label}}
<button @click="removeFilter(filter)" :aria-label="'Remove ' + filter.label + ' filter'">
<rebuy-icon name="x"></rebuy-icon>
</button>
</span>
</div>
<!-- Skeleton Loading -->
<div v-if="isLoading" class="rebuy-smart-collections__skeleton rebuy-smart-collections__skeleton--grid">
<div v-for="n in 12" class="rebuy-smart-collections__skeleton-item">
<div class="rebuy-smart-collections__skeleton-image"></div>
<div class="rebuy-smart-collections__skeleton-text"></div>
<div class="rebuy-smart-collections__skeleton-text short"></div>
</div>
</div>
<!-- Products Grid -->
<div v-else-if="products && products.length > 0" class="rebuy-smart-collections__products-grid">
<div v-for="(product, index) in products" :key="product.id" class="rebuy-smart-collections__product-card">
<!-- Product Image -->
<a :href="productUrl(product)" class="rebuy-smart-collections__product-image" @click="handleProductView(product, index)">
<img
:src="productImage(product)"
:srcset="generateImgSrcset(product)"
:alt="product.name"
loading="lazy"
/>
<span v-if="product.featured_badge" class="rebuy-smart-collections__badge">{{product.featured_badge}}</span>
</a>
<!-- Product Info -->
<div class="rebuy-smart-collections__product-info">
<a :href="productUrl(product)" class="rebuy-smart-collections__product-title" @click="handleProductView(product, index)">
{{product.name}}
</a>
<!-- Reviews -->
<div v-if="shouldShowReviews(product)" class="rebuy-smart-collections__product-reviews" aria-label="Product rating">
<span class="rebuy-star-rating">
<span class="rebuy-star-rating-background"></span>
<span class="rebuy-star-rating-foreground" :style="{ width: getReviewPercentage(product) }"></span>
</span>
<span class="rebuy-review-count">({{product.reviews.count}})</span>
</div>
<!-- Price -->
<div class="rebuy-smart-collections__product-price">
<span v-if="productOnSale(product)" class="rebuy-money sale">
<span class="sr-only">Sale price</span>
<span v-html="formatMoney(product.selected_variant.price)"></span>
</span>
<span v-if="productOnSale(product)" class="rebuy-money compare-at">
<span class="sr-only">Original price</span>
<span v-html="formatMoney(product.selected_variant.compareAtPrice)"></span>
</span>
<span v-else class="rebuy-money">
<span class="sr-only">Price</span>
<span v-html="formatMoney(product.selected_variant.price)"></span>
</span>
</div>
<!-- Variant Selector -->
<div v-if="shouldShowVariantSelector(product)" class="rebuy-smart-collections__variant-selector">
<label :for="'variant-' + product.id" class="sr-only">Select variant</label>
<select :id="'variant-' + product.id" v-model="product.selected_variant_id" @change="selectVariant(product)">
<option v-for="variant in product.variants" :value="variant.id">{{variant.name}}</option>
</select>
</div>
<!-- Add to Cart -->
<button
v-if="shouldShowAddToCart(product)"
class="rebuy-button rebuy-smart-collections__add-to-cart"
:class="{ working: product.status === 'adding' }"
:disabled="!variantAvailable(product.selected_variant) || product.status === 'adding'"
:aria-label="buttonAriaLabel(product)"
@click="addToCart(product)"
>
<span v-html="buttonLabel(product)"></span>
</button>
</div>
</div>
</div>
<!-- No Results -->
<div v-else class="rebuy-smart-collections__no-results">
<p>No products found matching your filters.</p>
<button @click="resetFilters()" class="rebuy-button">Clear Filters</button>
</div>
<!-- Pagination -->
<div v-if="shouldShowPagination()" class="rebuy-smart-collections__pagination">
<button
v-if="hasPreviousPage()"
@click="goToPage(currentPage - 1)"
class="rebuy-smart-collections__pagination-btn"
aria-label="Previous page"
>
<rebuy-icon name="chevron-left"></rebuy-icon>
Previous
</button>
<div class="rebuy-smart-collections__pagination-pages">
<button
v-for="page in visiblePages"
:key="page"
@click="goToPage(page)"
class="rebuy-smart-collections__pagination-page"
:class="{ active: page === currentPage }"
:aria-current="page === currentPage ? 'page' : null"
>
{{page}}
</button>
</div>
<button
v-if="hasNextPage()"
@click="goToPage(currentPage + 1)"
class="rebuy-smart-collections__pagination-btn"
aria-label="Next page"
>
Next
<rebuy-icon name="chevron-right"></rebuy-icon>
</button>
</div>
<!-- Load More -->
<div v-if="shouldShowLoadMore()" class="rebuy-smart-collections__load-more">
<button @click="loadMore()" :disabled="isLoadingMore" class="rebuy-button">
{{isLoadingMore ? 'Loading...' : 'Load More Products'}}
</button>
</div>
</main>
</div>
<!-- Mobile Filter Flyout -->
<div v-if="mobileFiltersOpen" class="rebuy-smart-collections__mobile-flyout" role="dialog" aria-modal="true" aria-label="Product filters">
<div class="rebuy-smart-collections__mobile-flyout-backdrop" @click="closeMobileFilters()"></div>
<div class="rebuy-smart-collections__mobile-flyout-panel">
<div class="rebuy-smart-collections__mobile-flyout-header">
<h3>Filters</h3>
<button @click="closeMobileFilters()" aria-label="Close filters">
<rebuy-icon name="x"></rebuy-icon>
</button>
</div>
<div class="rebuy-smart-collections__mobile-flyout-body">
<!-- Same filter sections as sidebar -->
</div>
<div class="rebuy-smart-collections__mobile-flyout-footer">
<button @click="resetFilters()" class="rebuy-smart-collections__reset-btn">Reset</button>
<button @click="closeMobileFilters()" class="rebuy-button">Apply Filters</button>
</div>
</div>
</div>
<!-- Powered by Rebuy -->
<div v-if="shouldShowPoweredByRebuy()" class="powered-by-rebuy">
<a :href="'https://rebuyengine.com/?shop=' + shop()" target="_blank" rel="noopener">
Powered by Rebuy
</a>
</div>
</div>
</div>
</script>
{% endraw %}
Template Features¶
| Feature | Description |
|---|---|
| Hero section | Collection title, description, and optional hero image |
| Sidebar filters | Persistent filter panel with collapsible sections |
| Mobile flyout | Dedicated filter panel for mobile devices |
| Skeleton loading | Placeholder UI while products load |
| Selected filters | Visual display of active filters with remove buttons |
| Product grid | Responsive product cards with images, reviews, pricing |
| Accessibility | ARIA labels, screen reader text, keyboard navigation |
Layout Structure¶
+------------------+------------------------+
| Hero Section |
+------------------+------------------------+
| | |
| Sidebar | Products Grid |
| Filters | |
| | - Sort controls |
| - Price | - Product cards |
| - Availability| - Pagination |
| - Tags | |
| - Vendors | |
| - Types | |
| | |
+------------------+------------------------+
Mobile Considerations¶
On mobile devices: - Sidebar hides and becomes a flyout panel - Filter button shows active filter count - Flyout includes Apply and Reset actions
Customization¶
Accessing Liquid¶
To access Liquid snippets within the template:
{% raw %}
<!-- Vue.js template code -->
{% endraw %}
{% render 'your-liquid-snippet' %}
{% raw %}
<!-- More Vue.js template code -->
{% endraw %}
CSS Classes¶
All elements use BEM-style class names prefixed with rebuy-smart-collections__. Override these in your theme CSS to customize appearance.