Customization
The Product Detail Page (PDP) offers a versatile framework that allows you to modify its layout and functionalities to suit your specific business objectives. Below, you will find key areas where customization is possible, whether through our SCAYLE Panel or by editing the source code directly.
If you haven’t explored it yet, we suggest starting with our General Overview of the PDP to gain a better understanding of its features and layout options.
Product Details
To add, remove, or modify product information on the PDP, you need to update the corresponding attributes in the SCAYLE Panel. Detailed instructions can be found in the Attribute Groups section of the Panel Guide.
Each product attribute has a type
field that categorizes the product information, which in turn determines the product information accordion displayed on the PDP.

Add a type for the attribute to ensure it is displayed as product information on the PDP.
When setting or modifying a type
, it's important to make some adjustments in the Boilerplate code. Currently, the displayed types are limited to the following:
const types = ['fit_size', 'extras', 'design'];
If you require additional types or need to remove existing ones, simply update this array by adding or removing the corresponding type
string as defined in the Panel.
The final step involves creating an i18n key for the accordion title. This is handled dynamically, allowing the type
value to retrieve the appropriate translation text. The current i18n keys are located under pdp -> information
, with examples such as:
"fit_size": "Fit & Size",
"extras": "Extras",
"design": "Design"
By adding a new type
along with its related translation, an accordion with the corresponding title will automatically be generated on the PDP.
Recommendation Slider for Different Types of Related Products
The ProductRecommendation
component offers great flexibility when it comes to displaying recommendations. While it currently utilizes the combineWith
attribute to showcase products, this is just one of many possibilities. The component itself is designed to handle any list of product IDs you provide, meaning you could easily integrate personalized recommendations driven by a user's viewing history or past purchases.
Diverse Ways to Utilize the ProductRecommendation Component
- Recently Viewed Products: Enhance the user experience by displaying a list of recently viewed products. This can help users quickly revisit items they have shown interest in, improving the likelihood of conversion.
- Frequently Bought Together: Leverage the component to showcase products that are often purchased together with the currently viewed item. This can encourage additional purchases and enhance the average order value.
- Similar Products: Implement a feature that displays similar products based on attributes such as brand, category, or price range. This provides users with alternative options and helps them find products that meet their preferences.
- Personalized Recommendations: Integrate personalized recommendations based on the user's browsing history or past purchases. This tailored approach can increase engagement and drive conversions by presenting items that align with the user's interests.
- Seasonal Promotions: Highlight seasonal products or promotions using the slider. For example, during holiday seasons, showcase relevant gift items or discounts to encourage timely purchases.
- Trending Products or Best Sellers: Use the slider to highlight trending or popular products within a specific category. This feature can attract user attention to items that are currently in demand.
You might also want to filter out certain products from the recommendation slider (e.g. sold-out products). This is possible by applying any filter you might want to the products
list inside ProductRecomendations.vue
.
<template>
...
<template #default>
<template v-if="status === 'success'">
<ProductCard
v-for="(product, index) in availableProducts || []"
:key="product.id"
:product="product"
multiple-images
class="w-1/2 shrink-0 px-2 md:w-1/3 lg:w-1/4"
@click="trackProductCardClick(product, index)"
/>
</template>
</template>
...
</template>
<script setup lang="ts">
//...
const availableProducts = computed(() => {
return products.value?.filter((product) => !product.isSoldOut)
})
//...
</script>
Similar Products Recommendation
The "Similar Products Recommendation" feature enhances the shopping experience by suggesting items related to the current product. This feature can increase conversion rates, improve user retention, and boost the average order value by presenting users with alternatives based on category and color, especially when the desired size is unavailable.
Use Case
This feature recommends products that share the same primary category and color as the main product on the PDP, helping users explore alternatives.
Technical Overview
To implement this feature, we will create a new component. Here’s how:
- Create the Component
Create a.vue
file in thecomponents/product
folder, naming itSimilarProductsRecommendation.vue
.\<template> <div></div> </template> <script setup lang="ts"> </script>
- Define Inputs
Define aproduct
prop to pass the current product to the component.\<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>
- Get Product Information
Use theuseProductBaseInfo
composable to get the primary category and color.<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>
- Fetch Similar Products
Use theuseProducts
composable to fetch products with matching category and color.\<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>
- Render the Products
Display the recommended products using theSFItemsSlider
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>
Outcome
The final implementation will show a slider of similar products based on the current product’s category and color, providing users with alternative options that may meet their needs.
.png)
Similar Products Recommendation
Enhancing User Experience with the Product Detail Pop-up
The Product Detail Pop-up offers a seamless way for users to preview products and add them to their basket without disrupting their shopping experience or navigating away from the current page. Initially implemented for Free Gift products, this versatile pop-up can be easily integrated into other product cards throughout the application.
For instance, you can implement the pop-up for product cards within the Recommendation Slider, allowing users to quickly view product details and add items to their cart directly from the recommendations. Additionally, the pop-up can be utilized on product cards displayed on the Product Listing Page, enhancing user interaction and streamlining the purchasing process.
.png)
Product Card with an "Add to Basket" Button
Creating Low Stock Urgency
To let a user know that there are only a few items of a variant left, you can check the current stock of the variant using activeVariant.value?.stock.quantity
. Once the current stock is below a certain threshold and activeVariant.value?.stock.isSellableWithoutStock
is set to false
, you could display a hint on the PDP to create more urgency to buy the item.
.png)
Low Stock Disclaimer
Price
The Price component can be customized through several methods, with the most common being props and slots. This flexibility allows for easy adjustments to how prices and reductions are displayed.
Slots Overview
The Price component provides three customizable slots:
- Default Slot
Use this slot to fully override the price section, including the price, badges, and reduction details. Available properties:classes
: Text classes that are determined by thesize
andtype
props.showPriceFrom
: A boolean indicating whether to display "price from."appliedReductions
: An array of applied reductions (AppliedReduction[]
).formatCurrency
: The currency formatting string.price
: ThePrice
object.totalPrice
: The total price as a string.promotionStyle
: The style for promotions (PromotionStyle
).
- Relative Reduction (Badges) Slot:
Displays percentage reductions as badges. The property available:relativeReductions
: An array of relative reductions (RelativeReductions[]
).
- Tax Information Slot
Displays tax information (i18n translation ofprice.including_vat
).
Props Overview
The component accepts several props to control its behavior and appearance:
price
(Price
- Required):
The primary price object that includes details about the product's price.promotion
(Promotion | null
- Optional):
Represents any active promotion associated with the product. It can benull
or omitted if there is no promotion.showTaxInfo
(boolean
- Default:false
):
Determines whether the tax information should be displayed.showPriceFrom
(boolean
- Default:false
):
Indicates if the label "starting from" (i18n:price.starting_from
) should be displayed before the price. This is useful when the price shown is a starting price.showBadges
(boolean
- Default:true
):
Controls the visibility of reduction badges that display percentage reductions.size
(Size
- Default:Size.MD
):
Defines the size of the text used for the price display. Possible values areSize.XS
,Size.SM
,Size.MD
,Size.LG
, andSize.XL
.type
('normal' | 'whisper' | 'loud'
- Default:'normal'
):
Specifies the font style for the price text:'normal'
: Uses a variable font weight.'whisper'
: Uses a semi-bold font style.'loud'
: Uses a bold font style.
- lowestPriorPrice (
LowestPriorPrice | undefined
Default:undefined
):
Specifies the lowest prior price.
Lowest Prior Price
In the EU, when offering a discount, traders are required to indicate the lowest price in the 30 days before the reduction. To display this, the lowest prior price needs to be passed with the withTax
and relativeDifferenceToPrice
set. This can be requested as part of the product/variant details via parameters defined in /constants/withParams.ts
.
.png)
Lowest Prior Price
Price Customization Function
For more advanced customizations, use the createCustomPrice
function from the product.ts
utility. This function allows you to modify the price object to suit your needs. It takes two parameters:
- The base
Price
object. - Partial overrides to modify specific properties.
The function merges the base price and overrides to return a customized Price
object.
Example: Customizing for Free Gifts
const price = computed(() => {
const nonGiftPrice = activeVariant.value
? getPrice(activeVariant.value)
: variantWithLowestPrice.value?.price
if (!nonGiftPrice) {
return
}
return createCustomPrice(nonGiftPrice, {
withTax: 0 as CentAmount,
appliedReductions: [
{
amount: {
absoluteWithTax: nonGiftPrice.withTax as CentAmount,
relative: 1,
},
type: 'relative',
category: 'promotion',
},
],
})
})
.png)
Free Gifts Price Display
In this example:
- The original price (
nonGiftPrice
) is set to0
(indicating a free gift). - A 100% discount (
relative: 1
) is applied, categorized as a promotion.
By using createCustomPrice
, the component will display 0
as the price and reflect the applied reduction, making it ideal for dynamic price adjustments, such as promotions.