docs
  1. SCAYLE Resource Center
  2. Developer Guide
  3. Customization
  4. Advanced Customization
  5. Similar Products Recommendation

Similar Products Recommendation

Introduction

The "Similar Products Recommendation" feature on a Product Detail Page can be a powerful instrument that enhances the shopping experience by suggesting items related to the one a user is currently viewing. This can help users find or buy more items that better meet their preferences or needs, compare similar products based on price, and find products that closely match their desires.

The benefits it brings to your business include:

  • Higher Conversion Rates: Showing similar products can increase the likelihood of conversion.
  • User Retention: A similar product slider will provide a more personalized shopping experience, which will benefit users' overall loyalty.
  • Increased Average Order Value: Users might add more products to the basket if they like multiple similar products or cannot decide on one.

The following use case focuses on building a "Similar Products Recommendation" feature based on other products that share the same primary product category and color as the main product, shown on the Product Detail Page. This feature is particularly important in scenarios where the desired product size the user is viewing on the Product Detail Page is unavailable, prompting the user to look for alternative options.

Technical Explanation

Since this is a new feature, we will create a new component.
For this, we need to create a new .vue file where the file name is our component name.

In our example, we will name our component SimilarProductsRecommendation.
All the product-related components are located in the components/product folder so that we can create a new file called SimilarProductsRecommendation.vue.

We will start by just defining an empty Vue component like this:

<template>
  <div></div>
</template>

<script setup lang="ts">
</script>

First, we need to define our inputs for the component.

Since we want to render similar products on the product detail page, we can define our input as just receiving the product the user is viewing.

To do this, we use the defineProps function from Vue like this:

<template>
  <div></div>
</template>

<script setup lang="ts">
import type { Product } from '@scayle/storefront-nuxt'

// Here, we define a `product` property that has the common Product type from the @scayle/storefront-nuxt package
const props = defineProps<{
  product: Product
}>()
</script>

Before we can fetch similar products, we need to figure out our product's color and primary category.

Since this functionality is already used in other places in the application, it already exists in the useProductBaseInfo composable.

<template>
  <div></div>
</template>

<script setup lang="ts">
import type { Product } from '@scayle/storefront-nuxt'
import { useProductBaseInfo } from '~/composables'

const props = defineProps<{
  product: Product
}>()

// We call our `useProductBaseInfo` composable and 
// use the return value `primaryCategory` and `color` from it
const { primaryCategory, color } = useProductBaseInfo(props.product)
</script>

As the next step, we need to fetch other products which also have this category and color attribute.

To do this, we can use the useProducts composable provided by the @scayle/storefront-nuxt package.

<template>
  <div></div>
</template>

<script setup lang="ts">
import type { Product } from '@scayle/storefront-nuxt'
import { useProducts } from '#storefront/composables'
import { useProductBaseInfo } from '~/composables'
import { computed } from '#imports'
import { PRODUCT_WITH_PARAMS } from '~/constants/product'

const props = defineProps<{
  product: Product
}>()

const { primaryCategory, colors } = useProductBaseInfo(props.product)

// Call the `useProducts` composable, which will fetch the corresponding products
// and return the response to us in the `data` variable.
const { data } = useProducts({
  params: () => ({
    // We want to fetch 12 products
    perPage: 12,
    // The `with` params state what additional data we want to get for each product
    // Here we just use the application's default which is used for all products in the application
    with: PRODUCT_WITH_PARAMS,
    // Filter for the primary category of the product
    category: primaryCategory.value?.categoryUrl ?? '/',
    where: {
      // We also set a color attribute filter so we only get products in the correct color
      attributes: [
        {
          type: 'attributes',
          key: 'color',
          values: [...(color.value?.id ? [color.value.id] : [])],
        },
      ],
    },
    // And we want to sort the products by a scayle smart sorting key.
    sort: {
      sortingKey: 'scayle:v1:recommended',
    },
  }),
})

// Here we extract our actual products from the response
//
// Since our response might also contain our current product,
// we need to filter this product out manually here.
const products = computed(
  () =>
    data.value?.products.filter(
      (similarProduct) => similarProduct.id !== props.product.id,
    ),
)
</script>

Now that we have similar products, we need to adjust the template part of our component and render the similar products to the user.

We, first of all, create a div which will act as our container. The flex-col class will lay out the children vertically and py-4 gives our container some padding on the top and the bottom.

We then also render a SFHeadline with a translated key, px-4 giving the headline some padding on the left and right side while mb-2 giving it some margin on the bottom to push down the actual product slider.

For the slider, we use the SFItemsSlider and also give this component some padding on the left and right. As the children, we render for every similar product a ProductCard component.

<template>
  <div class="flex flex-col py-4">
    <SFHeadline class="mb-2 px-4">
      {{ $t('similar_products') }}
    </SFHeadline>
    <SFItemsSlider spaced-items spaced-width="sm" class="px-4">
      <ProductCard
        v-for="similarProduct in products"
        :id="similarProduct.id"
        :key="`product-recommendation-${similarProduct.id}`"
        class="w-64"
        :product="similarProduct"
        :listing-meta-data="{
          name: 'Product Recommendation List',
          id: 'ProductRecommendationList',
        }"
      />
    </SFItemsSlider>
  </div>
</template>

<script setup lang="ts">
import type { Product } from '@scayle/storefront-nuxt'
import { useProducts } from '#storefront/composables'
import { useProductBaseInfo } from '~/composables'
import { computed } from '#imports'
import { PRODUCT_WITH_PARAMS } from '~/constants/product'

const props = defineProps<{
  product: Product
}>()

const { primaryCategory, color } = useProductBaseInfo(props.product)

const { data } = useProducts({
  params: () => ({
    perPage: 12,
    with: PRODUCT_WITH_PARAMS,
    category: primaryCategory.value?.categoryUrl ?? '/',
    where: {
      attributes: [
        {
          type: 'attributes',
          key: 'color',
          values: [...(color.value?.id ? [color.value.id] : [])],
        },
      ],
    },
    sort: {
      sortingKey: 'scayle:v1:recommended',
    },
  }),
})

const products = computed(
  () =>
    data.value?.products.filter(
      (similarProduct) => similarProduct.id !== props.product.id,
    ),
)
</script>

The final outcome of the customization looks like this:

Similar Products Recommendation