Product Detail Page
Overview
The Product Detail Page (PDP) serves as the focal point for displaying detailed information about an individual product within the Storefront Boilerplate. It offers users a comprehensive view of product attributes, including images, descriptions, pricing and promotions, and available variants (such as size or color). In addition to essential features like add-to-cart functionality, the PDP includes shipping details, subscription functionality, and related product suggestions, enabling users to make well-informed purchasing decisions.
The functionalities outlined below are available starting from version SFB 1.4.0. Dive in and discover all the new features and enhancements we have implemented. Happy exploring!
Key Features
- Product Information Display: Provides detailed descriptions, pricing, and product specifications.
- Product Images: Displays multiple images, including zoom and gallery views for enhanced product visualization.
- Product Variants: Supports options like size and color.
- Add-to-Cart Functionality: Allows users to quickly add items to their shopping cart for seamless purchasing.
- Store Availability: Offers real-time updates on product availability in physical stores.
- Related Products: Recommends similar or complementary products to encourage further exploration.
- Promotions: Showcases active discounts, special offers, and promotional pricing for the product.
- Subscriptions: Enables subscription-based purchasing options for products that can be bought on a recurring basis.
.png)
Product Detail Page Key Features (1-5)
.png)
Product Detail Page Key Features (6)
.png)
Product Detail Page Key Features (7)
.png)
Product Detail Page Key Features (8)
Data flow
When a user accesses a PDP, the primary product data is fetched using the product ID present in the URL. If the product has configured recommendations in the combineWith
attribute, the data for those recommended products will also be loaded and displayed within the ProductRecommendations
component.
The page also retrieves a list of active promotions. If the product is associated with a promotion via the promotion
attribute, the relevant promotion will be applied to the PDP. In the case of a "Buy X get Y" promotion, the the data for the free gift product will be also fetched.
Upon page load, the default quantity will be set to 1
. No active variant (size) will be pre-selected, unless the product is available in only one size. Every time the active variant is changed, the quantity resets to 1
and can be adjusted, but it cannot exceed the available stock or a maximum of 10
, due to basket limitations on the maximum quantity of a single variant.
Components
Image Gallery
.png)
Image Gallery on Desktop
The image gallery is presented as a slider with arrow navigation. On desktop screens, thumbnails are displayed on the left side of the selected image, allowing users to browse images more quickly. Hovering over a thumbnail replaces the main image with the hovered one while clicking on the image opens it in a pop-up where users can zoom in. The same zoom-in functionality is available on mobile screens.
To ensure a smooth user experience, product images can be zoomed in on independently of the rest of the webpage. This is achieved through CSS transforms to directly scale the image. On the desktop, users can click within the ProductGalleryZoom
modal to control the zoom level, while on mobile, users can use the familiar pinch gesture for zooming in and out.
To prevent unintended zooming of the entire page on mobile, the default browser behavior for gestures must be disabled using the following event listeners:
useEventListener(document, 'gesturestart', (e) => {
e.preventDefault()
})
useEventListener(document, 'gesturechange', (e) => {
e.preventDefault()
})
To still allow pinch-to-zoom functionality for the images, the usePinch
utility from @vueuse/gesture is used. The zoom level is capped to a reasonable range by mapping the offset value to a scale between 1 and 2.
const MAX_PINCH_ZOOM = 500
const MIN_ZOOM = 1
const MAX_ZOOM_MOBILE = 2
const pinchHandler = ({ offset: [pinchZoom], pinching }:
{ offset: [number, number]; pinching: boolean }) => {
pinchActive.value = pinching
if (!pinching) {
return
}
scale.value = mapNumberToRange(
pinchZoom,
0,
MAX_PINCH_ZOOM,
MIN_ZOOM,
MAX_ZOOM_MOBILE,
)
// ...
}
usePinch(pinchHandler, {
domTarget: zoomElement,
eventOptions: {
passive: true,
},
})
Breadcrumbs
Breadcrumbs are displayed above the product brand and name section. By default, this component is visible only on a desktop, but you can include it in the mobile layout to simplify navigation. Regardless of the previous page visited before landing on the PDP, the breadcrumbs will always display the longest category path from the associated categories.
For example, if the product belongs to the following three categories:
- Women (1st level)
- Sneaker (2nd level)
- Sneaker Low (3rd level)
The breadcrumb structure would appear as Women | Sneaker | Sneaker Low.
If multiple longest category paths are available, the first one is displayed. For instance, in addition to the example above, if the product also belongs to:
- Men (1st level)
- Sneaker (2nd level)
- Sneaker Low (3rd level)
Then, the available paths are Women | Sneaker | Sneaker Low and Men | Sneaker | Sneaker Low. In this case, the first path (Women) will be shown.
Each category in the breadcrumb path is clickable, navigating users to the relevant Product Listing Page. Categories at different levels are separated by a designated separator to enhance clarity.
Price
The Price component (ProductPrice.vue
) is central to the Storefront Boilerplate, ensuring a consistent price display across the user journey. It supports various pricing models, including discounted, promotional, original, and free pricing.
Usage:
- Product Price: Displays the main product price.
- Product Subscription: For subscription-based pricing.
- Size Selector: Displays prices for different product variants.
- Product Detail Pop-up: Shows product pricing in a quick view without leaving the page.
Price Component Variants
The Price component supports multiple configurations, accommodating different pricing strategies. Key elements include the price amount, currency, and tax details. Depending on the pricing variant, additional elements may include:
- Final and original prices
- Lowest prior price (last 30 days)
- Discount and promotion percentages
- Badges for free products
- Labels like "from" for price differences across variants
Below are the key price variants supported in the Storefront Boilerplate.
Original Price
This is the standard price when no promotions or sales are applied, and the price is consistent across all sizes.
Size-Dependent Pricing
For products where the price varies by size, the lowest price is shown initially.
- Before selecting a size: Displays the lowest price with a "from" label (e.g., from 94.90 € incl. VAT).
- After selecting a size: The price updates based on the selected variant (e.g., 99.90 € incl. VAT).
Reduced Prices
Reduced prices reflect a discount from the original price. The original price is shown alongside the reduced price for comparison, which encourages customer purchases. We also display the 30-Day Best Price and the percentage difference compared to the lowest price in the last 30 days, as required by law.
.png)
Lowest Prior Price
Key use cases:
- Sales
- Campaigns
- Promotions
Sale & Campaign Price
Both sale and campaign prices are displayed with a red discount badge, showing the percentage off and the original price as strikethrough text.
-(1).png)
Sale or Campaign Price
The reduced price is indicated by the appliedReductions
. The appliedReductions.category
defines the type of reduction —either "sale" or "campaign" — and differentiates between these reductions.
The original price is calculated on the frontend. For instance, if the withTax
property from the getProductById
response indicates the final price (e.g., 23.92 €), the original price is determined by adding appliedReduction.amount.absoluteWithTax
to the final price. In this case:
- Final price: 23.92 €
- Reduction amount: 5.00 €
- Original price: 28.92 €
The discount badge will show the percentage reduction (appliedReduction.amount.relative
), which in this SAPI response example would be 14%.
{
"appliedReductions": [
{
"category": "sale",
"type": "relative",
"amount": {
"relative": 0.14,
"absoluteWithTax": 500
}
}
],
"currencyCode": "EUR",
"recommendedRetailPrice": null,
"tax": {
"vat": {
"amount": 1196,
"rate": 0.999999
}
},
"withTax": 2392,
"withoutTax": 1196
}
Promotion Price
Promotion prices are displayed after promotion conditions are met. These prices are not shown by default and update after the product is added to the basket.
You can find more details on the various promotion types and their display logic on our dedicated Promotions page.
.png)
Promotion Price
Key behaviors:
- Default display: Shows the original or size-dependent price (e.g., 119.00 € incl. VAT or from 94.90 € incl. VAT).
- After adding to basket: Displays the promotion price with a discount badge and updated final price in red.
- On selecting a size: If the selected size is in the basket, the promotion price is displayed. Otherwise, the default price remains until the promotion is applied.
Reductions & Promotions Combinations
Campaign + Sale Price
This variant shows both campaign and sale discounts applied. Two discount badges are displayed, followed by the final price, price before the first reduction, and price before the second reduction.
- Discount badges: We start by displaying two discount badges, using
appliedReduction.amount.relative
. In this case, the values will be 14% and 20%. - Final price: The final price, derived from the
withTax
property, will be displayed next. In this example, the final price is 23.92 €. - Price before the first reduction: For the first reduction, labeled as "sale," add the
absoluteWithTax
from the sale reduction to the final price:
23.92 € + 5.00 € = 28.92 €. - Price before the second reduction: To get the price before both reductions, add the
absoluteWithTax
from the campaign reduction to the previous price:
28.92 € + 5.98 € = 34.90 €.
The final display will show both discounts, the original price before reductions, and the final price after the reductions have been applied.
.png)
Combined Campaign and Sale Price
{
"appliedReductions": [
{
"category": "sale",
"type": "relative",
"amount": {
"relative": 0.14,
"absoluteWithTax": 500
}
},
{
"category": "campaign",
"type": "relative",
"amount": {
"relative": 0.2,
"absoluteWithTax": 598
}
}
],
"currencyCode": "EUR",
"recommendedRetailPrice": null,
"tax": {
"vat": {
"amount": 1196,
"rate": 0.999999
}
},
"withTax": 2392,
"withoutTax": 1196
}
Sale + Promotion Price or Campaign + Promotion Price
This combination displays a sale or campaign discount along with a promotion. The promotion badge appears first, followed by the sale or campaign badge. The final price reflects both reductions.
Sale + Campaign + Promotion Price
This variant integrates both sale and campaign discounts with a promotion. The promotion badge appears first, followed by the sale or campaign badge, and the final price reflects all reductions.
.png)
Combined Campaign, Sale and Promotion Price
Siblings
.png)
Color Siblings
The current implementation supports displaying only color variants in the sibling section. The section title reflects the active color, which is also the first one displayed in the sibling row. Sold-out siblings are indicated by a grey overlay. If the number of sibling items exceeds the width of the section, horizontal scrolling is enabled. Clicking on a sibling navigates the user to the PDP of that specific sibling.
Promotion
For additional information about promotions, check out our dedicated Promotions page.
Size, Quantity and Add to Basket
The process for adding items to the basket follows these steps:
- No Size Selected: If the user clicks "Add to Basket" without selecting a size, they will be automatically scrolled to the size selector, which will open and prompt them to choose a variant to proceed. For one-sized products, the size is preselected, so clicking "Add to Basket" will directly add the variant to the basket.
- Size and Quantity Selection: Once a size is selected, the user can specify the desired quantity. The minimum quantity is always 1, while the maximum is determined by the available quantity of the selected product variant.
- Success or Error Notifications: After attempting to add an item to the basket, a toast message will be displayed to inform the user of the result.
If successful, an i18n key basket.notification.add_to_basket_success
will trigger the success message.
In case of errors, different messages are shown based on the type of error:
- For HTTP Error 412 (variant out of stock), the i18n message
basket.notification.add_to_basket_variant_out_of_stock_error
is used. - For HTTP Error 413 (exceeding maximum basket items), the i18n message
basket.notification.add_to_basket_max_basket_items_error
is triggered. - For any other errors, the default message
basket.notification.add_to_basket_error
will be displayed.
export const getBasketToastErrorMessageKey = (error: unknown) => {
if (error instanceof FetchError) {
if (error.response.status === HttpStatusCode.PRECONDITION_FAILED) {
return 'basket.notification.add_to_basket_variant_out_of_stock_error'
} else if (error.response.status === HttpStatusCode.PAYLOAD_TOO_LARGE) {
return 'basket.notification.add_to_basket_max_basket_items_error'
}
}
return 'basket.notification.add_to_basket_error'
}
Subscriptions
For more details on subscription, please visit our dedicated Subscription page.
Store locator
To learn more about how to check the availability of products in physical stores, please refer to our Omnichannel page.
Product Details
Product specifications, including design, extras, shipping information, and other attributes, are displayed below the image gallery and product details in an accordion format. The following logic governs how these attributes are presented:
- Attributes with a defined
type
are included in the accordion. - Each attribute of the same
type
is grouped together under a single accordion section. - The values of attributes within the same type are combined and separated by commas.
Example:
Consider the following attributes:
"style": {
"id": 7066,
"key": "style",
"label": "Style",
"type": "design",
"multiSelect": false,
"values": {
"id": 2430,
"label": "Urban",
"value": "urban"
}
},
"sneakerStyle": {
"id": 7075,
"key": "sneakerStyle",
"label": "Style of trainer",
"type": "design",
"multiSelect": false,
"values": {
"id": 2546,
"label": "Running",
"value": "running"
}
}
Both attributes have the type
of "design" and will be grouped under one accordion titled "Design". The resulting display would look like this:
- Design:
- Style: Urban
- Style of trainer: Running
Special Case for type="extras"
Attributes with the type "extras" are displayed differently:
- The values are shown directly under the accordion without the label.
- Only the values themselves are listed.
Example:
"extras": {
"id": 7064,
"key": "extras",
"label": "Extras",
"type": "extras",
"multiSelect": true,
"values": [
{
"id": 2480,
"label": "Perforation",
"value": "perforation"
},
{
"id": 2481,
"label": "Padded shaft edges",
"value": "padded_shaft_edges"
}
]
}
This will render an accordion titled "Extras" with the following values:
- Perforation
- Padded shaft edges
Fixed Accordion for Shipping and Return
The content for "Shipping and Returns" is not derived from product attributes; instead, the values in this section are defined as i18n keys for localization purposes.
For further customization details, visit the customization page of PDP.
Recommendations
The recommendation slider displays associated products, such as similar items, complementary products, or those from the same brand. In the Storefront Boilerplate, it showcases recommended products.
To begin using the recommendation feature, you first need to define an advanced attribute group called combineWith
in your SCAYLE Panel setup. For detailed instructions on how to create this group, please refer to the guide here.
After creating the combineWith
group, enter the Product IDs in the "Combined With" tab on the SCAYLE Panel’s Product page. You can add multiple IDs for recommended products on the PDP.

Product in the SCAYLE Panel
The ProductRecommendations
component will display on the PDP if at least one Product ID is specified, fetching the necessary data to show the products.
<ProductRecommendations
v-if="recommendedProductIds.length"
:product-ids="recommendedProductIds"
:title="$t('global.product_recommendation')" />
.png)
Product Recommendations on the PDP
Sold-out State
.png)
Sold-out Product Detail Page
A product is considered sold out when the boolean attribute isSoldOut
on the product object is set to true. Sold-out products are excluded from the product streams on the Product Listing Pages but can still be accessed through their direct URL or via color siblings.
While users are unable to purchase sold-out items, they can still view product images and add these items to their wishlist. The sold-out status is clearly indicated by a "Sold Out" banner, and the "Add to Basket" button is disabled to prevent further attempts to purchase.
Product Detail Pop-up
Want a quick product preview and the ability to add items to your basket without navigating away from the page? This is easily achieved with the Product Detail Pop-up.
The pop-up currently displays essential information and actions, including the product image, name, brand, price, and buttons for "Add to Basket," "Add to Wishlist," along with a link to the full Product Detail Page.
In the current implementation, the pop-up can be triggered by clicking on the enabled Free Gift on PDPs that meet the conditions of the "Buy X Get Y" promotion. This feature enhances the user experience by allowing customers to quickly view product details and take action without interrupting their shopping flow.
.png)
Product Detail Pop-up
Customization Opportunities
To discover the customization possibilities for your PDP page, please check out our Customization Guide.