Home
Wiki
Home
Wiki
  1. 2. Components
  • Back to home
  • 1. Themes
  • Vs Code
    • Getting Started
  • Kitchenware
    • Layout
      • New Layout
      • Legacy Layout
    • Components
      • Announcement
      • Banner Carousel
      • Banner With Products Carousel
      • Blog Category List
      • Blog List
      • Brand List
      • Brands Carousel
      • Breadcrumb
      • Call To Action
      • Cart
      • Categories List
      • Change Password
      • Checkout
      • Cookie Manager
      • Filter list
      • Footer
      • Forgot Password
      • Form
      • Hero Carousel
      • Icon Block
      • Invitation
      • Last Visited Products
      • Layout
      • Login
      • Map
      • Nav Bar
      • Offer
      • Product Attachments
      • Product Attributes
      • Product Documentation
      • Product Expected
      • Product Modal
      • Products Block
      • Products Carousel
      • Product Single
      • Profile
      • Quote
      • Register
      • Related Products
      • Search
      • Stores
      • Subscribe Newsletter
      • Text with Image
      • Top Bar
      • Video
    • Reusables
      • Getting Started
    • Assets
      • Getting Started
    • SDK
      • Products
        • _findProductsByCategory
        • _findProductsByIds
        • _findProductsByTitle
        • _findProductsByFilter
        • _findProductsByCriteria
        • _findProductsAndCalculate
        • _findProductsThenCalculate
        • _getProductAttributeSet
        • _setLastVisited
      • Categories
        • _findCategoryTreeById
        • _findCategoriesByIds
        • _findCategoryByAlias
        • _findCategoryTreeByAlias
        • _getCategoryContent
      • Collections
        • _getCollectionContent
        • _findCollectionsByIds
        • _findCollectionsByIdsThenCalculate
      • Brands
        • _getBrandContent
        • _findBrandsByIds
      • Cart
        • _addToCartMulti
        • _addToCart
        • _setCart
        • _clearCart
        • _setCartListener
        • _removeFromCart
        • _calculateCart
      • Checkout
        • _startCheckout
        • _updateCheckout
        • _completeCheckout
      • Shopping Lists
        • _getShoppingLists
        • _updateShoppingList
        • _createShoppingList
        • _deleteShoppingList
        • _getShoppingListByAlias
      • Navigation
        • _getFooterMenu
        • _getHeaderMenu
      • Users
        • _getUserById
      • Utils
        • _calculateCurrency
        • _getCurrencySymbol
        • _getCulture
        • _subscribeToNewsletter
        • _findUnitsByIds
  • Noir
    • 0. Introduction
    • 1. Structure
      • Overview
      • LayoutA.liquid
      • ComponentsList.liquid
      • Metas.liquid
      • CssVariables.liquid
      • Json.liquid
      • GoogleTagManager.liquid
      • StagingButton.liquid
    • 2. Components
      • Overview
      • Announcement
      • BannerCarousel
      • BlogCategoryList
      • BlogList
      • BrandList
      • Breadcrumb
      • Cart
      • CategoriesList
      • ChangePassword
      • Checkout
      • CookieManager
      • FilterList
      • Footer
      • ForgotPassword
      • Form
      • IconBlock
      • Invitation
      • LastVisitedProducts
      • Login
      • Map
      • NavBar
      • ProductAttachments
      • ProductAttributes
      • ProductComparison
      • ProductDocumentation
      • ProductMixList
      • ProductsBlock
      • ProductsCarousel
      • ProductSingle
      • Profile
      • Register
      • RelatedProducts
      • SingleBlog
      • Stores
      • TextWithImage
      • ThankYouPage
      • TopBar
      • Wishlist
    • 3. Reusables
      • Overview
      • Addresses
      • BillingRetail
      • AddressForm
      • AnnouncementModal
      • BackToTop
      • Company
      • General
      • Login
      • LoginModal
      • Orders
      • Payment
      • ProductAttachments
      • ProductAttributes
      • ProductComparisonButton
      • ProductComparisonFloatingButton
      • ProductGridItem
      • ProductListItem
      • ShoppingListsButton
      • ProductModal
      • ProfileInfo
      • PromptModal
      • Register
      • Shipping
      • ShoppingLists
      • ShoppingListsNavbar
      • Toast
      • Users
      • VariantContent
      • WishlistButton
      • Services
    • 4. Assets
      • Fonts
      • Images
      • Templates
      • Javascript
        • Overview
        • theme.js
      • Css / Scss
        • Overview
        • ThemeClasses
    • 5. SDK
      • Overview
      • LiquidGlobals
      • ServicesSDK
  1. 2. Components

LastVisitedProducts

Purpose#

The LastVisitedProducts component renders a “Recently viewed products” carousel.
It supports:
Optional section header (title)
Rendering products as a Swiper slider
Rendering each product card via Reusables\ProductGridItem\Default
GA4 list tracking:
sends viewItemList only when the carousel enters the viewport (IntersectionObserver-style helper)

Inputs (model contract)#

Model shape (storefront example)#

{
  "products": [
    {
      "vatIncluded": true,
      "vatRate": 25,
      "reducedVatRate": 5,
      "unitText": "Sample text",
      "isMpnVisible": true,
      "isSkuVisible": true,
      "title": "Sample product title",
      "alias": "sample-product-alias",
      "status": "Active",
      "categoryId": "Category Id",
      "categoryName": "Sample category name",
      "categoryLink": "/category/sample",
      "additionalCategoryIds": [],
      "tags": [
        "sample-tag",
        "..."
      ],
      "pathCategories": [
        "Path Category Id",
        "..."
      ],
      "stockAvailabilityModelId": "Stock Availability Model Id",
      "isBundle": false,
      "id": "Product Id",
      "companyId": "Company Id",
      "link": "product/sample-product",
      "mediaItems": [
        {
          "id": "Media Item Id",
          "link": "https://example.com/media/sample.jpg",
          "position": 0,
          "alt": "sample.jpg",
          "mediaType": "Image"
        },
        "..."
      ],
      "maxPrice": 50,
      "minPrice": 35,
      "maxRetailPrice": 25,
      "minRetailPrice": 25,
      "updateDate": "2025-01-01T00:00:00.0000000+00:00",
      "insertDate": "2025-01-01T00:00:00.0000000+00:00",
      "variantCount": 24,
      "additionalFeatures": {
        "icoTags": [
          {
            "id": "IcoTag Id",
            "name": "Sample text"
          },
          "..."
        ]
      },
      "searchAttributes": [
        "Sample search attribute",
        "..."
      ],
      "productVariants": [
        {
          "quantityConstraints": {
            "additive": {
              "minimum": 1,
              "maximum": 999999,
              "step": 1,
              "isValid": true
            },
            "absolute": {
              "minimum": 1,
              "maximum": 999999,
              "step": 1,
              "isValid": true
            }
          },
          "id": "Variant Id",
          "mediaItem": {},
          "unitPrice": 25,
          "price": 25,
          "quantity": 0,
          "retail": {
            "price": 25,
            "unitId": "(UNDEFINED)",
            "unitPrice": 25
          },
          "dimension1ItemId": "Attribute Item Id",
          "dimension2ItemId": "Attribute Item Id",
          "sellOutOfStock": true,
          "unitId": "Unit Id",
          "requiresShipping": false,
          "translation": {},
          "canOrder": true,
          "additionalFeatures": {},
          "bundleItems": [],
          "shoppingLists": [],
          "startQuantity": 1,
          "finalPriceText": "Sample price text",
          "finalPrice": 25
        },
        "..."
      ],
      "attributes": [
        {
          "attributeId": "Attribute Id",
          "attributeItemId": "Attribute Item Id",
          "attributeItemValue": "Sample text",
          "name": "Sample text",
          "slug": "sample-slug",
          "usedAsFilter": false,
          "displayOnProduct": true,
          "displayOnList": true,
          "displayOnCompare": true
        },
        "..."
      ],
      "icoTags": [
        {
          "id": "IcoTag Id",
          "name": "Sample text",
          "icon": {
            "alt": "Sample text"
          }
        },
        "..."
      ],
      "labels": [
        {
          "id": "Label Id",
          "name": "Sample text",
          "icon": {
            "alt": "Sample text"
          }
        },
        "..."
      ],
      "dimension1": {
        "id": "Dimension Id",
        "attributeId": "Attribute Id",
        "name": "Sample text",
        "translation": {},
        "type": "Color",
        "usedAsFilter": true,
        "displayOnProduct": true,
        "displayOnList": true,
        "displayOnCompare": true,
        "items": [
          {
            "textColor": "#000000",
            "id": "Attribute Item Id",
            "value": "Sample text"
          },
          "..."
        ]
      },
      "dimension2": {
        "id": "Dimension Id",
        "attributeId": "Attribute Id",
        "name": "Sample text",
        "translation": {},
        "type": "Size",
        "usedAsFilter": true,
        "displayOnProduct": true,
        "displayOnList": true,
        "displayOnCompare": true,
        "items": [
          {
            "id": "Attribute Item Id",
            "value": "S"
          },
          "..."
        ]
      },
      "startPriceText": "Sample price text",
      "startPrice": 25,
      "hasPriceRange": false,
      "inWishlist": false
    },
    "..."
  ],
  "name": "LastVisitedProducts",
  "view": "Default",
  "section": "SectionA",
  "settings": {
    "id": "Component Id",
    "section": "SectionA",
    "type": "NoirLastVisitedProducts",
    "name": "LastVisitedProducts",
    "configuredInContentApi": true,
    "view": "Default",
    "displayName": "Sample text",
    "cssClass": "(UNDEFINED)",
    "header": "Sample text",
    "alignment": "Left"
  },
  "translations": {
    "lastVisitedProducts": "Sample text"
  }
}

Required fields#

settings.id
Used for deterministic DOM ids:
wrapper: comp-{{ id }}
swiper container: products-carousel-{{ id }}
pagination: .swiper-pagination-{{ id }}
products[]
The component renders only when products exists and products.size > 0.

Optional fields#

settings.cssClass
Applied only when non-empty and not (UNDEFINED).
settings.header
If present, rendered as the section header (title).
If missing, list name falls back to translations.lastVisitedProducts.
settings.alignment
Controls header alignment classes:
Left (default)
Center
Right

JavaScript#

Global object#

The component exposes:
It returns Alpine state and initializes:
Swiper
“view item list” GA event when in view

initComponent(id, products, headerTitle)#

What it does
Factory that receives:
id: component id (used to locate DOM nodes)
products: serialized products array (used for analytics payload)
headerTitle: header or translation fallback (used as GA listName)
Returns:
id, products, headerTitle
lifecycle init()
method swiperInit(id)
Advice
Keep the factory signature stable; the Liquid template passes these values directly.

init()#

What it does
1.
Initializes the Swiper carousel:
this.swiperInit(this.id)
2.
Registers an “in view” callback using a helper:
elementInView("#products-carousel-" + id, callback, { threshold: 0.25 })
When the carousel becomes visible enough, it:
prepares GA items:
prepareListProducts(this.products)
sends GA event:
sendGAEvent("viewItemList", { listName: headerTitle, items })
What it means in practice
GA “view list” is not fired immediately on page load.
It fires only when the user scrolls and the carousel is at least ~25% visible, which usually improves analytics accuracy.
Advice
elementInView(...) is assumed to be a global helper (typically defined in Assets/js/theme.js). If it’s missing, analytics won’t fire (but the carousel still works).
If you want the event to fire sooner/later, adjust the threshold.

swiperInit(id)#

What it does
Finds carousel: #products-carousel-<id>. If missing → return.
Reads slides and pagination element:
slides are counted to decide looping
Sets Swiper rules:
mobile: slidesPerView = 1
desktop (≥ 1024): slidesPerView = 4
loop enabled only if slides.length > 4
spaceBetween = 24
pagination enabled and clickable
Advice
Loop is conditional to avoid Swiper loop issues when there aren’t enough slides.
Swiper must be loaded globally; otherwise new Swiper(...) will throw.

Global Alpine stores (used by LastVisitedProducts)#

This component does not directly use Alpine stores like cart, toast, etc.
It is mostly “local state + Swiper + analytics” driven.
However, it indirectly depends on global utilities typically defined in the theme runtime:
elementInView(...)
prepareListProducts(...)
sendGAEvent(...)
Notes
If you later want click tracking, it’s usually implemented in the ProductGridItem reusable (since it has listName/position).

Dependencies#

Alpine.js
Swiper (CSS + JS)
Global theme utilities:
elementInView
prepareListProducts
sendGAEvent

Notes#

Even though it is called “LastVisitedProducts”, the component does not itself compute “last visited” on the client; it simply renders the products[] provided in the view model.
Product variant color behaviour is influenced by the computed colorExists flag.

Extras#

Template behavior (Liquid + Alpine)#

Render conditions#

If products is empty/missing, the entire section is not rendered.

Color detection for product cards#

The template computes a boolean colorExists by checking if any product has:
product.dimension1.type == 'Color' OR
product.dimension2.type == 'Color'
This flag is passed into Reusables\ProductGridItem\Default to influence how variant color UI is rendered.

Carousel structure#

Always renders Swiper markup:
.swiper container with .swiper-wrapper and .swiper-slide items
pagination element: .swiper-pagination-{{ id }}

Product card rendering#

Each slide renders:
{% render 'Reusables\\ProductGridItem\\Default', product: product, modelId: id, colorExists: colorExists, listName: listName, position: forloop.index0 %}
That means:
analytics list name (listName) and position are passed down to the product card reusable.

Alpine initialization#

The wrapper mounts the JS factory:
x-data='lastvisitedproductsdefault.initComponent(id, products, headerOrTranslation)'
Modified at 2026-04-14 13:18:56
Previous
Invitation
Next
Login
Built with