---
title: Displaying product details
related:
  - https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products.md
  - https://docs.kentico.com/13/developing-websites/implementing-routing/custom-routing-using-url-patterns.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

When building e-commerce sites, you often need to display [product catalog](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/displaying-product-listings.md) and product details pages. Product details pages usually include more comprehensive information about specific [products](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products.md).

This page describes how to display products from the **Products** application in a live site application connected to Xperience.

- [Displaying product details pages](#displaying-product-details-pages)
- (Optional) [Getting URLs of product pages from SKU data](#getting-urls-of-product-pages-from-sku-data)

Note that the examples were created for a site that uses [content tree-based routing](https://docs.kentico.com/13/developing-websites/implementing-routing/content-tree-based-routing.md).

## Displaying product details pages

> **Tip:** **Tip**: View the full code of a functional example in the [LearningKit project](https://github.com/Kentico/LearningKit-Mvc) on GitHub. You can also run the LearningKit website by connecting the project to a Xperience database. Follow the instructions in the repository's README file.

1. Open your live site project in Visual Studio.
2. Add a generic view model representing products. Only include the properties that you wish to display or otherwise require. Create separate view models if you require properties specific to a given product.

   ```csharp

           public readonly PriceDetailViewModel PriceDetail;
           public readonly string Name;
           public readonly string Description;
           public readonly string ShortDescription;
           public readonly int SKUID;
           public readonly string ImagePath;
           public readonly bool IsInStock;

           /// <summary>
           /// Creates a new product model.
           /// </summary>
           /// <param name="productPage">Product's page.</param>
           /// <param name="priceDetail">Price of the product.</param>
           public ProductViewModel(SKUTreeNode productPage, ProductCatalogPrices priceDetail)
           {
               // Fills the page information   
               Name = productPage.DocumentName;
               Description = productPage.DocumentSKUDescription;
               ShortDescription = productPage.DocumentSKUShortDescription;

               // Fills the SKU information
               SKUInfo sku = productPage.SKU;
               SKUID = sku.SKUID;
               ImagePath = string.IsNullOrEmpty(sku.SKUImagePath) ? null : new FileUrl(sku.SKUImagePath, true)
                                                                               .WithSizeConstraint(SizeConstraint.MaxWidthOrHeight(400))
                                                                               .RelativePath;

               IsInStock = sku.SKUTrackInventory == TrackInventoryTypeEnum.Disabled ||
                           sku.SKUAvailableItems > 0;

               PriceDetail = new PriceDetailViewModel()
               {
                   Price = priceDetail.Price,
                   ListPrice = priceDetail.ListPrice,
                   CurrencyFormatString = priceDetail.Currency.CurrencyFormatString
               };
           }


   ```
3. Add a new controller with an action that displays the product details page. The example also retrieves the coupled product page.

   ```csharp title="Initializing services"

           /// <summary>
           /// Constructor for the ProductController class.
           /// </summary>
           public ProductController(IShoppingService shoppingService,
                                    ICatalogPriceCalculatorFactory priceCalculatorFactory,
                                    IPageDataContextRetriever pageRetriever,
                                    ISiteService siteService,
                                    ISKUInfoProvider skuInfoProvider)
           {
               // Initializes instances of services required to manage product price calculation and the shopping cart
               this.shoppingService = shoppingService;
               this.priceCalculatorFactory = priceCalculatorFactory;
               this.pageRetriever = pageRetriever;
               this.siteService = siteService;
               this.skuInfoProvider = skuInfoProvider;
           }


   ```

   > **Tip:** We recommend using a [dependency injection container](https://docs.kentico.com/13/developing-websites/initializing-xperience-services-with-dependency-injection.md) to initialize instances of used API services (e.g. _IShoppingService_ _)._

   ```csharp

           /// <summary>
           /// Displays a product detail page of a product.
           /// </summary>
           public ActionResult Detail()
           {
               // Gets the product from the data context
               SKUTreeNode product = GetProduct();

               // If the product is not found or if it is not allowed for sale, redirects to error 404
               if ((product == null) || (product.SKU == null) || !product.SKU.SKUEnabled)
               {
                   return HttpNotFound();
               }

               // Initializes the view model of the product with a calculated price
               ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

               ProductCatalogPrices price = catalogPriceCalculatorFactory
                   .GetCalculator(cart.ShoppingCartSiteID)
                   .GetPrices(product.SKU, Enumerable.Empty<SKUInfo>(), cart);

               // Fills the product model with retrieved data
               ProductViewModel viewModel = new ProductViewModel(product, price);

               // Displays the product details page
               return View("Detail", viewModel);
           }

           /// <summary>
           /// Retrieves the product.
           /// </summary>
           private SKUTreeNode GetProduct()
           {
               // Gets the current page from the router data context
               TreeNode node = pageRetriever.Retrieve<TreeNode>().Page;

               // If the found page is not a product, returns null
               if (node == null || !node.IsProduct())
               {
                   return null;
               }

               // Loads specific fields of the product's product page type from the database
               node.MakeComplete(true);

               // Returns the found page as a product page
               return node as SKUTreeNode;
           }


   ```
4. Add a view that sets the appearance of the product details page.

   ```csharp

   @model LearningKit.Models.Products.ProductViewModel

   <h2>@Model.Name</h2>

   @* Renders product details. *@
   @if (!string.IsNullOrEmpty(Model.ImagePath))
   {
       <img src="@Model.ImagePath" alt="@Model.Name">
   }
   <ul>
       <li>
           Description: @Html.Raw(@Model.Description)
       </li>
       <li>
           In stock:
           @if (!Model.IsInStock)
           {
               <span id="stockMessage">Yes</span>
           }
           else
           {
               <span id="stockMessage">No</span>
           }
       </li>
       <li>
           Total price: <span id="totalPrice">@String.Format(Model.PriceDetail.CurrencyFormatString, Model.PriceDetail.Price)</span>
       </li>
   </ul>


   ```

   Include a button that adds the product to a shopping cart. The button in the example calls the  **AddItem**  POST action from the  **CheckoutController**  implemented in  [Integrating the shopping cart](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/implementing-a-checkout-process/integrating-the-shopping-cart.md) as part of the [Implementing a checkout process](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/implementing-a-checkout-process.md) series.

   ```csharp

       @* Renders an add to cart button. *@
       using (Html.BeginForm("AddItem", "Checkout", FormMethod.Post))
       {
           <input type="hidden" name="itemSkuId" value="@Model.SKUID" />
           <label>Qty</label>
           <input type="text" name="itemUnits" value="1" />
           <input type="submit" name="AddItem" value="Add to cart" />
       }


   ```

Customers can now browse product details pages. They are also able to add a product to their shopping cart directly from a product's detail page.

> **Tip:** You can access the product page type's fields through generated model classes. See more in [Generating classes for Xperience objects](https://docs.kentico.com/13/developing-websites/generating-classes-for-xperience-objects.md).

## Getting URLs of product pages from SKU data

When generating links to product details pages from pages that only hold a product's SKU information, you first need to retrieve the coupled product page and use it to build a qualified URL:

1. Edit the controller class you want to modify.
2. Add an action that retrieves a product page from a given SKU identifier.

   ```csharp

           /// <summary>
           /// Redirects to a product detail page based on the ID of a product's SKU object.
           /// </summary>
           /// <param name="skuID">ID of the product's SKU object.</param>
           public ActionResult ItemDetail(int skuID)
           {
               // Gets the SKU object
               SKUInfo sku = skuInfo.Get(skuID);

               // If the SKU does not exist or it is a product option, returns error 404
               if (sku == null || sku.IsProductOption)
               {
                   return HttpNotFound();
               }

               // If the SKU is a product variant, uses its parent product's ID
               if (sku.IsProductVariant)
               {
                   skuID = sku.SKUParentSKUID;
               }

               // Gets the product's page
               TreeNode node = pageRetriever.Retrieve<TreeNode>(query => query
                                   .Culture("en-us")
                                   .CombineWithDefaultCulture()
                                   .WhereEquals("NodeSKUID", skuID))
                                   .FirstOrDefault();

               // Returns 404 if no page for the specified product exists
               if (node == null)
               {
                   return HttpNotFound();
               }

               // Gets the product page's URL 
               string pageUrl = pageUrlRetriever.Retrieve(node).AbsoluteUrl;

               return Redirect(pageUrl);
           }


   ```

   > **Tip:** If you do not use [product variants](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/displaying-product-details/displaying-product-variants.md) in your store, you can move the variant check to the condition that returns a Not Found response, or remove it completely.
3. Add a link to the **ItemDetail** action method to views where you only have access to the SKU identifier value.

   ```csharp

   @Html.ActionLink(model.SKUName, "ItemDetail", new { skuId = model.SKUID })


   ```

   OR

   ```xml

   <a href="@Url.Action("ItemDetail", new { skuId = model.SKUID })">link text</a>


   ```

You are now able to build valid URLs to product details pages even when you do not have the product's page information available.
