---
title: Displaying product details
related:
  - https://docs.kentico.com/k12sp/e-commerce-features/managing-on-line-stores/products.md
  - https://docs.kentico.com/k12sp/developing-websites/configuring-page-urls-on-mvc-sites.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 an MVC e-commerce site, you may want to display [product catalog](https://docs.kentico.com/k12sp/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/k12sp/e-commerce-features/managing-on-line-stores/products.md).

This page describes how to display products from the **Products** application in an MVC application connected to Kentico.

To build product detail pages in the Kentico MVC framework, you need to ensure the following:

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

## Displaying product details pages

To display product details pages:

> **Tip:** **Tip**: To view the full code of a functional example, you can inspect and download the [LearningKit project](https://github.com/Kentico/LearningKit-Mvc) on GitHub. You can also run the LearningKit website by connecting the project to a Kentico database.

1. Open your MVC 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 Guid ProductPageGuid;
           public readonly string ProductPageAlias;
           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;
               ProductPageGuid = productPage.NodeGUID;
               ProductPageAlias = productPage.NodeAlias;

               // Fills the SKU information
               SKUInfo sku = productPage.SKU;
               SKUID = sku.SKUID;
               ImagePath = sku.SKUImagePath;
               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()
           {
               // Initializes instances of services required to manage product price calculation and the shopping cart
               // For real-world projects, we recommend using a dependency injection container to initialize service instances
               shoppingService = Service.Resolve<IShoppingService>();
               calculatorFactory = Service.Resolve<ICatalogPriceCalculatorFactory>();
           }


   ```

   > **Tip:** We recommend using a [dependency injection container](https://docs.kentico.com/k12sp/developing-websites/initializing-kentico-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 specified by the GUID of the product's page.
           /// </summary>
           /// <param name="guid">Node GUID of the product's page.</param>
           /// <param name="productAlias">Node alias of the product's page.</param>
           public ActionResult Detail(Guid guid, string productAlias)
           {
               // Gets the product from the connected Kentico database
               SKUTreeNode product = GetProduct(guid);

               // 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();
               }

               // Redirects if the specified page alias does not match
               if (!string.IsNullOrEmpty(productAlias) && !product.NodeAlias.Equals(productAlias, StringComparison.InvariantCultureIgnoreCase))
               {
                   return RedirectToActionPermanent("Detail", new { guid = product.NodeGUID, productAlias = product.NodeAlias });
               }

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

               ProductCatalogPrices price = calculatorFactory
                   .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(viewModel);
           }

           /// <summary>
           /// Retrieves the product specified by GUID of the product's page.
           /// </summary>
           /// <param name="nodeGuid">Node GUID of the product's page.</param>
           private SKUTreeNode GetProduct(Guid nodeGuid)
           {
               // Gets the page with the node GUID
               TreeNode node = DocumentHelper.GetDocuments()
                               .LatestVersion(false)
                               .Published(true)
                               .OnSite(SiteContext.CurrentSiteName)
                               .Culture("en-US")
                               .CombineWithDefaultCulture()
                               .WhereEquals("NodeGUID", nodeGuid)
                               .FirstOrDefault();

               // 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="@Url.Kentico().ImageUrl(Model.ImagePath, SizeConstraint.MaxWidthOrHeight(500))" 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  [Using a shopping cart on MVC sites](https://docs.kentico.com/k12sp/e-commerce-features/developing-on-line-stores/implementing-a-checkout-process/using-a-shopping-cart-on-mvc-sites.md) as part of the [Implementing a checkout process](https://docs.kentico.com/k12sp/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 Kentico objects](https://docs.kentico.com/k12sp/developing-websites/generating-classes-for-kentico-objects.md).

## Customizing URLs of product details pages

To provide SEO-friendly URLs, create a custom route according to your specifications.

For example, if you want to have your product details pages follow this pattern: _\<your domain>/Product/\<node guid>/<[node alias](https://docs.kentico.com/k12sp/managing-website-content/working-with-pages/setting-page-aliases-for-content-only-pages.md)>_, register the following route in the **RouteConfig** class in the **App\_Start** folder:

```csharp

            routes.MapRoute(
                name: "Product",
                url: "Product/{guid}/{productAlias}",
                defaults: new { controller = "Product", action = "Detail" },
                constraints: new { guid = new GuidRouteConstraint() }
            );


```

The example assumes that the MVC application displays product details pages with a _Product_ controller and its _Detail_ action (as used in [Displaying product details pages](#displaying-product-details-pages)).

## 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 its properties 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 = SKUInfoProvider.GetSKUInfo(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 = DocumentHelper.GetDocuments()
                   .LatestVersion(false)
                   .Published(true)
                   .OnSite(siteName)
                   .Culture("en-us")
                   .CombineWithDefaultCulture()
                   .WhereEquals("NodeSKUID", skuID)
                   .FirstOrDefault();

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

               // Redirects to the product details page action method with the product information
               return RedirectToAction("Detail", "Product", new
               {
                   guid = node.NodeGUID,
                   productAlias = node.NodeAlias
               });
           }


   ```

   > **Tip:** If you do not use [product variants](https://docs.kentico.com/k12sp/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.
