Skip to main content

Marqo Storefront Search Widget -- User Guide

Configuration guide for merchants and agencies customizing the Marqo search widget on Shopify storefronts.


1. Getting Started

The Marqo storefront search widget replaces Shopify's default search and collection pages with AI-powered product results. It renders a product grid with filters, sorting, pagination, and customizable product cards directly on your storefront.

What the widget provides

  • Product grid with configurable columns (mobile/tablet/desktop), card styling, and image carousels
  • Filter sidebar with categorical, range, hierarchical, and stock availability filters
  • Sort dropdown with configurable sort options (price, relevance, etc.)
  • Pagination with page numbers or continuous scroll
  • Product cards with images, titles, vendors, prices, sale badges, star reviews, and CTA buttons
  • Grid injections for promo banners, video tiles, and editorial content between products
  • DOM events for integrating quickshop modals, analytics, and third-party widgets

How it appears on storefronts

The widget embeds into a container element on search and collection pages. On search pages, it takes over the results area. On collection pages, it replaces the collection product grid. The widget automatically detects the correct container via the Embed Element selector configured in the admin UI.

First-time setup

  1. Install the Marqo app on your Shopify store
  2. Open the Marqo admin dashboard
  3. Navigate to Settings to configure the widget appearance
  4. Use Live Preview to see changes in real time before publishing
  5. Save and publish to push settings to your storefront

Customization tiers

The widget supports four tiers of customization, from easiest to most advanced:

TierMechanismWhoWhere
1. Dashboard knobs~121 UI settings (colors, fonts, layout, toggles)Non-technical merchantAdmin UI
2. Custom CSSCSS editor with full selector accessMerchant with CSS knowledgeAdmin UI
3. Advanced Template EditorPer-component HTML/CSS editingDeveloperAdmin UI
4. Theme Template OverridesVue templates in Shopify theme codeDeveloper/agencyShopify theme editor

Tiers 1 and 2 always apply regardless of which tier provides the HTML template. CSS variables and custom CSS are injected independently.


2. Dashboard Settings (Tier 1)

All settings are accessible from the admin UI. Changes are reflected in the live preview immediately.

Layout

Controls the overall page structure and search bar.

SettingTypeWhat it controls
Show Search BarToggleWhether to display the search input above results
Search Bar PlaceholderTextPlaceholder text in the search input (e.g., "Search for products")
Page Title TemplateTextTemplate for the results heading. Use {count} for total results and {term} for the search query. Example: {count} results found for "{term}"
Results Count FontFont pickerFont family for the results count text
Results Count SizeNumber (px)Font size for the results count text
Results Count WeightDropdownFont weight for the results count text
Results Count ColorColor pickerColor for the results count text

Filters

Controls the filter sidebar appearance and behavior.

Filter Items:

SettingTypeWhat it controls
FieldDropdownWhich product field to filter on (e.g., productVendor, productType, priceMin, productTags)
LabelTextDisplay name shown to shoppers (e.g., "Brand", "Price")
TypeDropdowncategorical (checkboxes) or range (min/max inputs with presets)
EnabledToggleShow or hide this filter
Array FieldToggleEnable for fields that contain arrays (like productTags)
Max ResultsNumberMaximum facet values to show for categorical filters (default: 100)
Range PresetsListQuick-select buttons for range filters (e.g., "Under $50", "$50-$100")

Filter Behavior:

SettingTypeWhat it controls
Icon StyleDropdownAccordion toggle icon: chevron or plus_minus
Show Product CountToggleShow the count of matching products next to each filter option
Show Selected FiltersToggleDisplay active filter pills above the results
Keep CollapsedToggleStart all filter sections collapsed
Show First ExpandedToggleExpand the first filter section even when others are collapsed
Filter ModeDropdownsidebar (always visible on desktop) or drawer (slide-in panel)
Stock Availability LabelsText fieldsLabels for "In Stock" and "Out of Stock" filter options

Filter Styling:

SettingCSS VariableWhat it controls
Background Color--marqo-filter-bgFilter sidebar background
Border Radius--marqo-filter-radiusCorner rounding of the filter container
Text Color--marqo-filter-text-colorColor for filter labels and option text
Text Font--marqo-filter-text-fontFont family for filter text
Text Size--marqo-filter-text-sizeFont size for filter text
Text Weight--marqo-filter-text-weightFont weight for filter text
Separator Color--marqo-filter-separatorColor of the divider lines between filter sections. Set to "none" for transparent.
Active Indicator Color--marqo-filter-active-indicatorHighlight color for checked filter options
Button Background--marqo-filter-btn-bgBackground of filter action buttons
Button Text Color--marqo-filter-btn-textText color of filter action buttons
Button Border Color--marqo-filter-btn-borderBorder color of filter action buttons

Active Filter Pill Styling:

SettingCSS VariableWhat it controls
Background--marqo-active-filter-bgBackground of active filter pills
Text Color--marqo-active-filter-textText color of active filter pills
Font Size--marqo-active-filter-sizeFont size of active filter pills
Border Radius--marqo-active-filter-radiusCorner rounding of active filter pills

Sorting

Controls the sort dropdown above the results grid.

SettingTypeWhat it controls
EnabledToggleShow or hide the sort dropdown
Default SortDropdownWhich sort option is selected by default
Sort OptionsListAvailable sort options with label and enabled toggle. Each has an id (e.g., priceMin-asc), label (e.g., "Price: Low to High"), and enabled toggle.

Sort Dropdown Styling:

SettingCSS VariableWhat it controls
Background--marqo-sort-dropdown-bgDropdown background color
Border Color--marqo-sort-dropdown-borderDropdown border color
Text Color--marqo-sort-dropdown-textDropdown text color

Product Display

Controls the product grid layout and card appearance.

Layout:

SettingCSS VariableWhat it controls
Image Ratio--marqo-card-image-ratioProduct image aspect ratio: 1:1, 3:4, or 9:16
Column Spacing--marqo-column-spacingGap between product cards in the grid (px)
Card Content Spacing--marqo-card-content-gapSpacing between elements inside a product card (px)
Columns (Mobile)--Number of grid columns on mobile (<480px). Default: 2
Columns (Tablet)--Number of grid columns on tablet (<1024px). Default: 3
Columns (Desktop)--Number of grid columns on desktop. Default: 4
Text Alignment--Alignment of text within product cards: left, center, or right

Card Styling:

SettingCSS VariableWhat it controls
Card Border Radius--marqo-card-border-radiusCorner rounding of product cards (px)
Card Border Width--marqo-card-border-widthBorder thickness of product cards (px)
Card Border Color--marqo-card-border-colorBorder color of product cards
Card Background--marqo-card-bgBackground color of product cards
Card Shadow--marqo-card-shadowBox shadow preset: none, subtle, medium, or strong
Card Hover Effect--marqo-card-hover-shadow, --marqo-card-hover-transform, --marqo-card-image-hover-transformHover behavior: none, shadow (shadow on hover), lift (card lifts up), or zoom (image zooms)

Text Styles:

Five text groups with four properties each:

GroupCSS Variable PrefixWhat it styles
Vendor--marqo-vendor-*Product vendor/brand name
Title--marqo-title-*Product title
Price--marqo-price-*Product price
Variant--marqo-variant-*Variant title (e.g., "Large / Blue")
Collections--marqo-collections-*Collection names

Each group has these properties:

PropertyCSS Variable SuffixWhat it controls
Font Family-fontFont family (e.g., inherit, "Helvetica Neue")
Font Size-sizeFont size in px
Font Weight-weightFont weight (e.g., 400, 500, 600, 700)
Color-colorText color

Sale Badge:

SettingCSS VariableWhat it controls
Enabled--Show or hide the sale badge on discounted products
Position--Badge position: top-left or top-right
Text Format--Badge text template. Use {percent} for the discount percentage. Example: SAVE {percent}%
Background Color--marqo-sale-badge-bgBadge background color
Text Color--marqo-sale-badge-textBadge text color
Border Radius--marqo-sale-badge-radiusCorner rounding of the badge (px)

Price Display:

SettingCSS VariableWhat it controls
Sale Price Color--marqo-price-sale-colorColor of the sale price (shown when item is on sale)
Compare-At Price Color--marqo-price-compare-colorColor of the original price with strikethrough
Show Compare-At Price--Whether to show the original price when an item is on sale

Image Carousel:

SettingTypeWhat it controls
EnabledToggleEnable prev/next arrow buttons for cycling through product images
Show ArrowsDropdownhover (arrows appear on mouse hover) or always (arrows always visible)
Arrow StyleDropdownVisual style of the carousel arrows: chevron or arrow

CTA (Call to Action)

Controls the button on each product card.

SettingCSS VariableWhat it controls
Enabled--Show or hide the CTA button
Behavior--navigate (link to product page) or add_to_cart (add to Shopify cart)
Button Text--Default button text (e.g., "Shop Now", "Add to Cart")
Adding Text--Text shown while adding to cart (e.g., "Adding...")
Added Text--Text shown after successful add (e.g., "Added!")
Sold Out Text--Text shown when product is sold out
Background Color--marqo-cta-bgButton background color
Text Color--marqo-cta-textButton text color
Border Color--marqo-cta-border-colorButton border color
Border Width--marqo-cta-border-widthButton border thickness (px)
Border Radius--marqo-cta-border-radiusButton corner rounding (px)
Full Width--marqo-cta-widthWhether the button spans the full card width

Pagination

Controls how shoppers navigate between pages of results.

SettingTypeWhat it controls
ModeDropdownpaging (numbered page buttons) or continuous_scroll (infinite scroll)
Items Per PageNumberNumber of products per page
Show Per-Page DropdownToggleLet shoppers choose how many products per page
Per-Page OptionsListAvailable values for the per-page dropdown (e.g., 12, 24, 36, 48, 60)

Pagination Dropdown Styling:

SettingCSS VariableWhat it controls
Background--marqo-page-dropdown-bgItems-per-page dropdown background
Border Color--marqo-page-dropdown-borderItems-per-page dropdown border color
Text Color--marqo-page-dropdown-textItems-per-page dropdown text color

Reviews

Controls the product review stars and integration with third-party review providers.

SettingCSS VariableWhat it controls
Enabled--Show or hide review stars on product cards
Provider--Review data source: okendo, trustpilot, or custom
Metafield Namespace--Shopify metafield namespace for custom review data
Review Count Key--Metafield key for the review count
Review Average Key--Metafield key for the average rating
Star Color--marqo-star-colorColor of filled review stars
Star Size--marqo-star-sizeSize of review stars (px)
Review Text Color--marqo-review-text-colorColor of the review count text
Show Count--Whether to display the review count number (e.g., "(42)") next to stars

When using Okendo or Trustpilot, the widget renders fallback star ratings from your indexed review data. After the page loads, the provider's own widget hydrates and replaces the fallback with their interactive component.

States

Controls the loading, error, and no-results states.

StateSettingsWhat it controls
LoadingEnabled, MessageWhat to show while search results are loading
ErrorEnabled, MessageWhat to show when the search request fails
No ResultsEnabled, MessageWhat to show when a search returns zero results

Selectors

Controls where the widget renders and which pages it runs on.

SettingTypeWhat it controls
Embed ElementCSS selectorWhere to inject the widget on the page. Example: #MainContent, #main-content, #main
Excluded PathsList of URL pathsPages where the widget should NOT run. Example: /cart, /account, /pages/about

3. Custom CSS (Tier 2)

The Custom CSS editor in the admin UI lets you write CSS that applies on top of the dashboard settings. Your CSS is injected after the widget's default styles, so it takes precedence.

How to use the CSS editor

  1. Navigate to Settings > Custom CSS in the admin UI
  2. Write CSS rules targeting .marqo-* classes
  3. Preview changes in real time
  4. Save to publish

Complete CSS variable reference

All CSS variables are scoped to .marqo-search-layout. You can override any variable in your custom CSS:

.marqo-search-layout {
--marqo-card-border-radius: 12px;
--marqo-title-color: #333333;
--marqo-cta-bg: #ff6600;
}

Sale Badge:

VariableDefaultControls
--marqo-sale-badge-bg#dc2626Badge background
--marqo-sale-badge-text#ffffffBadge text color
--marqo-sale-badge-radius4pxBadge border radius

CTA Button:

VariableDefaultControls
--marqo-cta-bg#000000Button background
--marqo-cta-text#ffffffButton text color
--marqo-cta-border-color#000000Button border color
--marqo-cta-border-width0pxButton border width
--marqo-cta-border-radius0pxButton border radius
--marqo-cta-width100%Button width (100% or auto)

Product Card:

VariableDefaultControls
--marqo-card-border-radius8pxCard corner rounding
--marqo-card-border-width1pxCard border thickness
--marqo-card-border-color#e2e8f0Card border color
--marqo-card-bg#ffffffCard background
--marqo-card-shadownoneCard box shadow
--marqo-card-hover-shadow0 4px 12px rgba(0,0,0,0.08)Shadow on hover
--marqo-card-hover-transformnoneTransform on hover (e.g., translateY(-2px) for lift)
--marqo-card-image-hover-transformnoneImage transform on hover (e.g., scale(1.05) for zoom)
--marqo-column-spacing16pxGap between cards
--marqo-card-content-gap6pxSpacing inside card content
--marqo-card-image-ratio1/1Image aspect ratio

Price:

VariableDefaultControls
--marqo-price-sale-color#dc2626Sale price color
--marqo-price-compare-color#9ca3afCompare-at (original) price color
--marqo-price-fontinheritPrice font family
--marqo-price-size16pxPrice font size
--marqo-price-weight700Price font weight
--marqo-price-regular-color#059669Regular (non-sale) price color

Text Styles:

VariableDefaultControls
--marqo-vendor-fontinheritVendor font family
--marqo-vendor-size12pxVendor font size
--marqo-vendor-weight500Vendor font weight
--marqo-vendor-color#64748bVendor text color
--marqo-title-fontinheritTitle font family
--marqo-title-size14pxTitle font size
--marqo-title-weight600Title font weight
--marqo-title-color#1e293bTitle text color
--marqo-variant-fontinheritVariant title font family
--marqo-variant-size12pxVariant title font size
--marqo-variant-weight400Variant title font weight
--marqo-variant-color#9ca3afVariant title text color
--marqo-collections-fontinheritCollections font family
--marqo-collections-size11pxCollections font size
--marqo-collections-weight400Collections font weight
--marqo-collections-color#b0b8c4Collections text color

Filters:

VariableDefaultControls
--marqo-filter-bg#ffffffFilter sidebar background
--marqo-filter-radius8pxFilter container border radius
--marqo-filter-text-color#1e293bFilter text color
--marqo-filter-text-size14pxFilter text font size
--marqo-filter-text-fontinheritFilter text font family
--marqo-filter-text-weight500Filter text font weight
--marqo-filter-separator#e2e8f0Filter section separator color
--marqo-filter-active-indicator#3b82f6Active filter indicator color
--marqo-filter-btn-bg#ffffffFilter button background
--marqo-filter-btn-text#1e293bFilter button text color
--marqo-filter-btn-border#e2e8f0Filter button border color

Active Filter Pills:

VariableDefaultControls
--marqo-active-filter-bg#f3f4f6Pill background
--marqo-active-filter-text#374151Pill text color
--marqo-active-filter-size12pxPill font size
--marqo-active-filter-radius4pxPill border radius

Sort Dropdown:

VariableDefaultControls
--marqo-sort-dropdown-bg#ffffffSort dropdown background
--marqo-sort-dropdown-border#e2e8f0Sort dropdown border
--marqo-sort-dropdown-text#1e293bSort dropdown text

Pagination Dropdown:

VariableDefaultControls
--marqo-page-dropdown-bg#ffffffPer-page dropdown background
--marqo-page-dropdown-border#e2e8f0Per-page dropdown border
--marqo-page-dropdown-text#1e293bPer-page dropdown text

Results Count:

VariableDefaultControls
--marqo-results-fontinheritResults count font family
--marqo-results-size14pxResults count font size
--marqo-results-weight400Results count font weight
--marqo-results-color#6b7280Results count text color

Reviews:

VariableDefaultControls
--marqo-star-color#f59e0bStar rating fill color
--marqo-star-size16pxStar rating size
--marqo-review-text-color#6b7280Review count text color

Responsive Grid (dynamic):

VariableContextControls
--marqo-grid-columnsSet per breakpointCurrent column count. Used internally to clamp grid injection span.

Common CSS examples

Rounded product cards with soft shadow:

.marqo-search-layout {
--marqo-card-border-radius: 16px;
--marqo-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
--marqo-card-border-width: 0px;
}

Custom CTA button styling:

.marqo-search-layout {
--marqo-cta-bg: #2563eb;
--marqo-cta-text: #ffffff;
--marqo-cta-border-radius: 24px;
--marqo-cta-border-width: 0px;
}

Hide vendor name and collection text:

.marqo-vendor { display: none; }
.marqo-collections { display: none; }

Custom filter sidebar width:

.marqo-filter-sidebar {
width: 280px;
min-width: 280px;
}

Style the sale badge:

.marqo-sale-badge {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 4px 10px;
}

Custom pagination button style:

.marqo-pagination-button.marqo-active {
background: #2563eb;
color: #ffffff;
border-radius: 50%;
}

4. Advanced Template Editor (Tier 3)

The Advanced Template Editor lets you customize the HTML and CSS of individual components. Each component has its own HTML template and CSS stylesheet stored in the settings record.

Per-component editing

Each component can be edited independently. Changing the product card template does not affect filters, pagination, or other components.

Available template variables

Product card templates have access to these variables:

VariableTypeDescription
{{ title }}stringDecoded product title
{{ vendor }}stringProduct vendor/brand name
{{ price }}stringFormatted current price (e.g., "$29.99")
{{ compareAtPrice }}stringFormatted original price when on sale (empty if not on sale)
{{ productUrl }}stringProduct page URL (e.g., /products/baked-starter-kit)
{{ imageUrl }}stringCurrent display image URL (carousel-aware)
{{ salePercent }}numberDiscount percentage (0 if not on sale)
{{ saleBadgeLabel }}stringFormatted sale badge text (e.g., "SAVE 30%")
{{ saleBadgePosition }}stringBadge position: top-left or top-right
{{ reviewAverage }}numberAverage star rating (0 if no reviews)
{{ reviewCount }}numberNumber of reviews
{{ reviewPercentage }}numberRating as percentage of 5 stars (for star fill width)
{{ ctaEnabled }}booleanWhether the CTA button is enabled
{{ ctaButtonText }}stringCTA button text
{{ shopifyProductId }}stringShopify product ID
{{ productId }}stringMarqo product/variant ID

All raw product fields from the search index are also available (e.g., {{ product.productTags }}, {{ product.variantTitle }}).

Available methods

Templates can use these methods with Vue event bindings:

MethodSyntaxDescription
addToCart(variantId, qty)@click="addToCart(product._id, 1)"Add to Shopify cart via /cart/add.js
formatPrice(cents){{ formatPrice(priceValue) }}Format a number as currency
carouselPrev()@click.prevent="carouselPrev"Navigate to previous image
carouselNext()@click.prevent="carouselNext"Navigate to next image

Vue directive reference

The template engine is Vue.js. These directives are available:

DirectivePurposeExample
v-ifConditionally render an element<span v-if="salePercent">On Sale!</span>
v-elseElse branch for v-if<span v-else>Regular Price</span>
v-forLoop over a list<li v-for="tag in product.productTags">{{ tag }}</li>
v-showToggle visibility (CSS display)<div v-show="hasMultipleImages">
:attr (v-bind)Dynamic attribute binding<img :src="imageUrl" :alt="title" />
@event (v-on)Event listener binding<button @click="carouselNext">Next</button>
v-htmlRender raw HTML (use with caution)<div v-html="customContent"></div>
{{ expr }}Text interpolation<span>{{ price }}</span>

Validation warnings

The editor validates your template and shows warnings for:

  • Missing required CSS classes (e.g., .marqo-product-card)
  • Invalid Vue syntax
  • References to undefined variables

Reset to default

Each component has a "Reset to Default" button that restores the original template. This does not affect other components.


5. Grid Injections

Grid injections let you insert non-product content (promo banners, video tiles, editorial blocks) at configurable positions within the product results grid.

How it works

Injections are siblings of product cards in the CSS grid. They appear between product cards at positions you specify.

+----------+ +----------+ +----------+ +----------+
|Product 1 | |Product 2 | |Product 3 | |Product 4 |
+----------+ +----------+ +----------+ +----------+
+-----------------------------------------------------+
| Grid Injection (position: 4, span: 4) |
| "Complete Your Routine -- Shop Routines" |
+-----------------------------------------------------+
+----------+ +----------+ +----------+ +----------+
|Product 5 | |Product 6 | |Product 7 | |Product 8 |
+----------+ +----------+ +----------+ +----------+

Configuration

  1. Go to Settings > Grid Injections in the admin UI
  2. Click Add Content Block
  3. Configure each injection:
SettingTypeWhat it controls
PositionNumberWhere to insert. 0 = before first product, 4 = after the 4th product. Relative to the current page.
Column SpanNumber (1-4)How many grid columns to span. 4 = full-width banner (for a 4-column grid). Clamped to the current column count at each breakpoint.
HTMLCode editorThe content HTML
CSSCode editorStyling for the content
EnabledToggleShow or hide this injection

Position behavior

  • Position 0: Before the first product card
  • Position N: After the Nth product card
  • Position > result count: Placed at the end of the grid
  • Negative positions: Clamped to 0
  • Positions are relative to the current page's result set, not the global result set

Column span

The span value controls how many grid columns the injection stretches across. It is automatically clamped at each breakpoint so it never exceeds the current column count. For example, span: 4 on a 2-column mobile layout becomes span: 2.

CSS scoping

Injection CSS is injected into a dedicated <style id="marqo-grid-injection-styles"> element. Use specific selectors scoped to your injection content:

/* Good -- scoped to injection content */
.marqo-grid-injection .my-banner { padding: 24px; }

/* Avoid -- affects the entire page */
.banner { padding: 24px; }

Examples

Static promo banner (full width):

Position: 4, Span: 4

HTML:

<div class="routine-banner">
<img src="https://cdn.shopify.com/s/files/banner.jpg"
alt="Complete Your Routine" />
<a href="/collections/routines">Shop Routines &rarr;</a>
</div>

CSS:

.routine-banner {
text-align: center;
padding: 32px 24px;
background: #faf5f0;
border-radius: 8px;
}
.routine-banner img {
max-width: 100%;
border-radius: 4px;
margin-bottom: 12px;
}
.routine-banner a {
color: #000;
font-weight: 600;
text-decoration: underline;
}

Video tile (Videowise integration):

Position: 8, Span: 2

HTML:

<div class="videowise-container" data-videowise-id="abc123"></div>

Then in theme.liquid, add a script to hydrate the video after the grid renders:

<script>
document.addEventListener('marqo:grid.injected', function() {
if (window.Videowise) window.Videowise.init();
});
</script>

Editorial content block:

Position: 12, Span: 2

HTML:

<div class="editorial">
<h3>Expert Tips</h3>
<p>Our makeup artists recommend starting with primer for all-day wear.</p>
<a href="/blogs/tips">Read More</a>
</div>

Limitations

  • No template variables: Unlike product card templates, injection HTML is static. It does not have access to {{ product }} data or search context. For dynamic content, use DOM events to populate containers after render.
  • No <script> execution: HTML inserted via innerHTML does not execute <script> tags. Use theme-level scripts with DOM event listeners instead.
  • CSS is global: Injection CSS is not automatically scoped. Use specific class selectors.
  • Sanitization: Inline event handlers (onclick, onerror, etc.), javascript: URIs, and dangerous tags (<script>, <iframe>, <object>, <embed>, <form>) are stripped for security.

6. DOM Events

The widget emits CustomEvents on document at key lifecycle points. Listen for these events from a <script> tag in your Shopify theme to add custom behavior.

Event reference

EventWhen it firesCancelableKey Payload Fields
marqo:readyWidget initializedNo{}
marqo:destroyWidget being torn downNo{}
marqo:search.startSearch request about to fireNo{ query, collection? }
marqo:search.resultsResults received and renderedNo{ products, query, total, facets, container }
marqo:search.emptyZero results returnedNo{ query }
marqo:search.errorSearch request failedNo{ error }
marqo:card.renderEach product card rendered (fires per card)No{ product, element, index }
marqo:cta.clickCTA button clickedYes{ product, handle, productUrl, shopifyProductId, element }
marqo:filter.changeFilter value changedNo{ field, type, value }
marqo:filter.clearAll filters clearedNo{}
marqo:sort.changeSort option changedNo{ sort }
marqo:page.changePage navigationNo{ page }
marqo:grid.injectedGrid injections renderedNo{ injections }

All events have bubbles: true. Only marqo:cta.click is cancelable.

Listening for events

Add a <script> tag to your theme.liquid before </body>:

<script>
document.addEventListener('marqo:search.results', function(e) {
console.log('Search complete:', e.detail.total, 'results for', e.detail.query);
});
</script>

Event payload details

marqo:search.start

{
query: string; // User's search query (empty string for collection pages)
collection?: string; // Collection name (only on collection pages)
}

marqo:search.results

{
products: MarqoSearchHit[]; // Array of product result objects
query: string; // Search query
total: number; // Total result count
facets: object; // Facet/filter data
container: HTMLElement; // The results grid DOM element
}

marqo:card.render

{
product: MarqoSearchHit; // Product data for this card
element: HTMLElement; // The card's DOM element
index: number; // Position in the results (0-indexed)
}

marqo:cta.click

{
product: MarqoSearchHit; // Full product data
handle: string; // Product handle (e.g., "baked-starter-kit")
productUrl: string; // "/products/baked-starter-kit"
shopifyProductId: string; // Shopify product ID
element: HTMLElement; // The CTA button DOM element
}

marqo:filter.change

{
field: string; // Filter field name (e.g., "productVendor")
type: string; // Filter type ("categorical" or "range")
value: any; // Selected value(s)
}

Cancelable events

marqo:cta.click is the only cancelable event. Calling e.preventDefault() stops the default navigation to the product detail page:

<script>
document.addEventListener('marqo:cta.click', function(e) {
e.preventDefault(); // Stop navigation to PDP
console.log('CTA clicked for:', e.detail.handle);
});
</script>

Use case: Quickshop modal

Bridge the CTA click to your theme's quickshop/quick-add component:

<script>
document.addEventListener('marqo:cta.click', function(e) {
e.preventDefault();

// Create or find the quickshop modal (theme-specific web component)
var modal = document.querySelector('#MarqoQuickAdd');
if (!modal) {
document.body.insertAdjacentHTML('beforeend',
'<quick-add-modal id="MarqoQuickAdd" class="quick-add-modal">' +
'<div role="dialog" class="quick-add-modal__content global-settings-popup" tabindex="-1">' +
'<button type="button" class="quick-add-modal__toggle" aria-label="Close">&times;</button>' +
'<div class="quick-add-modal__content-info"></div>' +
'</div>' +
'</quick-add-modal>'
);
modal = document.querySelector('#MarqoQuickAdd');
}

e.detail.element.setAttribute('data-product-url', e.detail.productUrl);
modal.show(e.detail.element);
});
</script>

Use case: Analytics integration

Track search and click events with your analytics provider:

<script>
document.addEventListener('marqo:search.results', function(e) {
analytics.track('search', {
query: e.detail.query,
resultCount: e.detail.total,
});
});

document.addEventListener('marqo:cta.click', function(e) {
analytics.track('product_click', {
handle: e.detail.handle,
position: e.detail.index,
});
});
</script>

Use case: Third-party widget hydration

Re-initialize Okendo, Trustpilot, Videowise, or other third-party widgets after Marqo renders new content:

<script>
document.addEventListener('marqo:search.results', function() {
// Re-initialize Okendo widgets on new cards
if (window.oke) window.oke.initWidget();
});

document.addEventListener('marqo:grid.injected', function() {
// Hydrate Videowise containers injected into the grid
if (window.Videowise) window.Videowise.init();
});
</script>

Use case: Custom card modifications

Modify product cards after they render (e.g., add custom badges):

<script>
document.addEventListener('marqo:card.render', function(e) {
var product = e.detail.product;
var element = e.detail.element;

// Add "New" badge to products tagged as new
if (product.productTags && product.productTags.includes('new')) {
var badge = document.createElement('span');
badge.className = 'custom-new-badge';
badge.textContent = 'NEW';
element.querySelector('.marqo-product-card-image').appendChild(badge);
}
});
</script>

7. Theme Template Overrides (Tier 4)

Theme template overrides let developers place custom Vue templates directly in the Shopify theme code. The widget detects these templates and uses them instead of the admin UI templates.

How it works

Place a <script> tag with a specific ID and type="text/template" in your theme.liquid file (before </body>). The widget checks for these override tags before rendering each component.

{% raw %} wrapping requirement

Vue's {{ variable }} syntax conflicts with Shopify Liquid's {{ variable }} syntax. You must wrap Vue templates in {% raw %}{% endraw %} to prevent Liquid from evaluating Vue expressions:

<script id="marqo-product-card-template" type="text/template">
{% raw %}
<div class="marqo-product-card">
<h3>{{ title }}</h3>
<span>{{ price }}</span>
</div>
{% endraw %}
</script>

Without {% raw %}, Liquid would try to evaluate {{ title }} as a Liquid variable (producing empty string) before Vue ever sees it.

Supported override IDs

Script IDOverrides
marqo-product-card-templateProduct card HTML
marqo-filters-templateFilter sidebar
marqo-pagination-templatePagination controls
marqo-sort-templateSort dropdown
marqo-loading-templateLoading state
marqo-error-templateError state
marqo-no-results-templateNo results state

Per-component independence

Each component checks for its own theme override independently. You can override product cards while letting filters, pagination, and sort use the admin UI templates:

<!-- Only override the product card -- everything else uses admin UI settings -->
<script id="marqo-product-card-template" type="text/template">
{% raw %}
<div class="marqo-product-card">
<span v-if="salePercent" class="marqo-sale-badge">SAVE {{ salePercent }}%</span>
<a :href="productUrl">
<img :src="imageUrl" :alt="title" />
<h3>{{ title }}</h3>
<span>{{ price }}</span>
</a>
<button v-if="ctaEnabled" @click="addToCart(product._id)">
{{ ctaButtonText }}
</button>
</div>
{% endraw %}
</script>

Fallback chain

The widget resolves templates with this priority:

Tier 4: Theme override (Shopify theme snippet) <-- checked first, wins if present
|
v (if not found)
Tier 3: Advanced Template Editor (admin UI) <-- editable from admin UI
|
v (if not found)
Tier 2: Custom CSS (additive -- always applied)
|
v (applied on top of)
Tier 1: Dashboard knobs (CSS variables -- always applied)

Tiers 1 and 2 always apply regardless of which tier provides the HTML template. CSS variables and custom CSS are injected independently.

Support boundary

Following industry standard practice:

TierSupport level
1. Dashboard knobsFully supported
2. Custom CSSSupported (basic guidance)
3. Advanced Template EditorSupported for default templates. Custom edits are your responsibility.
4. Theme template overridesUnsupported -- "at your own discretion"

Custom template work falls outside the scope of the Marqo support team. Using custom templates is at your own discretion.


8. Troubleshooting

Blank results after filter click

Symptom: Clicking a filter clears the product grid and shows nothing.

Possible causes:

  • The filter field name does not match the actual Marqo index field. Check the Field value in filter settings matches the indexed field name exactly (case-sensitive).
  • The filter is configured as categorical but the field contains numeric data (or vice versa). Check the filter Type setting.
  • The filter field is an array type (like productTags) but Array Field is not enabled.

Fix: Verify filter field names match your index schema. Check the browser console for error messages from [Marqo Search].

Loading stuck / spinner never clears

Symptom: The loading spinner appears and never goes away.

Possible causes:

  • The Marqo API is unreachable. Check the browser Network tab for failed requests.
  • The embed element selector does not match any element on the page. Verify the Embed Element CSS selector is correct.
  • The page URL matches an excluded path. Check the Excluded Paths setting.

Fix: Open the browser developer console and look for [Marqo Search] error messages. Check the Network tab for API request failures.

CSP (Content Security Policy) errors

Symptom: Browser console shows Content Security Policy errors. Widget may not render.

Possible causes:

  • The storefront's CSP policy blocks inline styles or scripts.
  • The widget injects CSS via <style> elements, which requires style-src 'unsafe-inline' or a nonce.

Fix: Review your Shopify theme's CSP headers. The widget uses runtime-only Vue (no eval() or template compilation at runtime), which is CSP-safe for scripts. However, inline styles may need CSP adjustment.

Grid injections not appearing

Symptom: Grid injections configured in the admin UI do not appear on the storefront.

Possible causes:

  • The injection's Enabled toggle is off.
  • The injection Position is larger than the number of results on the page.
  • The injection HTML is empty.

Fix: Verify the injection is enabled and has HTML content. Listen for the marqo:grid.injected event in the console to confirm the injection is being processed.

Reviews not showing (Okendo / Trustpilot)

Symptom: Star ratings show but the third-party review widget does not hydrate.

Possible causes:

  • The review provider's script has not loaded yet when the widget renders.
  • The data-oke-reviews-product-id attribute format does not match what Okendo expects.

Fix: The widget waits 1 second before attempting to hydrate review widgets. If the provider loads later, listen for marqo:search.results and call the provider's init function:

<script>
document.addEventListener('marqo:search.results', function() {
if (window.oke) window.oke.initWidget();
if (window.Trustpilot) {
document.querySelectorAll('[data-trustpilot-widget]').forEach(function(el) {
window.Trustpilot.loadFromElement(el);
});
}
});
</script>

Debug mode: Monitor all Marqo events

Paste this snippet in the browser console to log every Marqo event as it fires:

[
'marqo:ready', 'marqo:destroy',
'marqo:search.start', 'marqo:search.results', 'marqo:search.empty', 'marqo:search.error',
'marqo:card.render', 'marqo:cta.click',
'marqo:filter.change', 'marqo:filter.clear', 'marqo:sort.change', 'marqo:page.change',
'marqo:grid.injected'
].forEach(name => {
document.addEventListener(name, (e) => {
console.log(`%c${name}`, 'color: #3b82f6; font-weight: bold', e.detail);
});
});

Then interact with the search page. Events appear in blue in the console with their payloads.

Note: marqo:ready fires once on widget init. If you paste the listener after the page has loaded, you will not see it. Refresh the page after pasting to catch it.