AI agents: the content index is at /llms.txt. Every page is also available as Markdown — send the request header Accept: text/markdown, or append .md to the page URL.
Xperience Docs
  • Community Questions & Answers (opens in a new tab)
  • Contact support (opens in a new tab)

Cookie consent

We use necessary cookies (opens in a new tab) to run our website and improve your experience while browsing. Additional cookies are only used with your consent. You may revoke your consent on the Cookies Policy (opens in a new tab) page or in your browser at any time.

Configure (opens in a new tab)
  • Documentation
    Administration interface basicsDevelopers and adminsBusiness usersChangelogGlossary
  • Guides
    Welcome to Xperience by KenticoAdministration interface basicsCustomer journeysDigital marketingConfigurationDevelopmentArchitectureUpgrade to Xperience by Kentico
  • Training
    For AdminsFor Business usersFor DevelopersFor Solution architects
  • API Examples
    ConfigurationContent managementDevelopmentDigital marketing
  • Changelog
    Documentation updatesSecurity advisories

Xperience by Kentico Developer kickstart

  • Overview
  • Set up an Xperience by Kentico project
  • Create a reusable content type
  • Add a website channel
  • Define a page content type
  • Configure the project to display content
  • Build a page template
  • Apply a page template
  • Create a layout view
  • Add a Contact us page
  • Model navigation
  • Implement navigation
  • Build a website with KentiCopilot
  • Next steps

Module: Xperience by Kentico Developer kickstart

12 of 14 Pages

Implement navigation

With the navigation content types in place, let's use a view component to render the Navigation menu that exists in the Content hub.

Create view models

Start by creating view models that correspond to the Navigation item and Navigation menu classes.

Add a new Navigation folder under Kickstart.Web/Features, to hold all the navigation-related files.

Inside, create the NavigationItemViewModel. It only needs two properties - Title and Url.

C#NavigationItemViewModel.cs
namespace Kickstart.Web.Features.Navigation;
public class NavigationItemViewModel
{
    public string Title { get; set; }

    public string Url { get; set; }
}

You might remember that in the page template step, you defined a method that created a LandingPageViewModel based on a LandingPage object.

We need a similar method here to create a NavigationItemViewModel from a NavigationItem.

You can retrieve the page's URL simply by calling the GetUrl extension method of IWebPageFieldsSource, which every web page implements. Return null if the provided item is null or missing its target.

C#NavigationItemViewModel.cs
using System.Linq;
using CMS.Websites;
using Microsoft.IdentityModel.Tokens;

namespace Kickstart.Web.Features.Navigation;

public class NavigationItemViewModel
{
    public string Title { get; set; }

    public string Url { get; set; }

    // new method
    public static NavigationItemViewModel GetViewModel(NavigationItem navigationItem)
    {
        if (navigationItem?.NavigationItemTarget == null || !navigationItem.NavigationItemTarget.Any())
        {
            return null;
        }

        var targetUrl = navigationItem.NavigationItemTarget.FirstOrDefault().GetUrl();

        return new NavigationItemViewModel
        {
            Title = navigationItem.NavigationItemTitle,
            Url = targetUrl.RelativePath
        };
    }
}

The NavigationMenuViewModel class also needs two properties: one to represent its Name and another to represent its Items.

Add a static method that returns a menu view model for the provided NavigationMenu object, calling NavigationItemViewModel.GetViewModel from above for each menu item.

C#NavigationMenuViewModel.cs
using System.Collections.Generic;
using System.Linq;

using Microsoft.IdentityModel.Tokens;

namespace Kickstart.Web.Features.Navigation;

public class NavigationMenuViewModel
{
    public string Name { get; set; }

    public IEnumerable<NavigationItemViewModel> Items { get; set; }

    public static NavigationMenuViewModel GetViewModel(NavigationMenu navigationMenu)
    {
        if (navigationMenu?.NavigationMenuItems == null || !navigationMenu.NavigationMenuItems.Any())
        {
            return null;
        }

        var menuItems = navigationMenu.NavigationMenuItems
            .Select(NavigationItemViewModel.GetViewModel)
            .Where(x => x != null);

        return new NavigationMenuViewModel
        {
            Name = navigationMenu.NavigationMenuDisplayName,
            Items = menuItems
        };
    }
}

Define a view component

Websites often have multiple navigation menus. Let's make a view component that renders the menu. You can reuse the view component across the site.

Define an asynchronous view component with a string parameter to indicate which menu item to retrieve.

C#NavigationMenuViewComponent.cs
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace Kickstart.Web.Features.Navigation;

public class NavigationMenuViewComponent : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync(string navigationMenuCodeName)
    {
        // A placeholder representing the logic that retrieves the `NavigationMenu` item from the database.
        var menu = await RetrieveMenu(navigationMenuCodeName);

        if (menu == null)
        {
            return View("~/Features/Navigation/NavigationMenuViewComponent.cshtml", new NavigationMenuViewModel());
        }

        var model = NavigationMenuViewModel.GetViewModel(menu);
        
        // We will define this view in the next step.
        return View("~/Features/Navigation/NavigationMenuViewComponent.cshtml", model);
    }
}

If you copy-paste the code above, you will see a couple of issues:

  1. The RetrieveMenu method does not exist - for now, we are using a placeholder to represent the logic that retrieves the menu item from the database.
  2. The ~/Features/Navigation/NavigationMenuViewComponent.cshtml view does not exist yet.

We will fix both of these shortly.

Add a private method to retrieve the menu.

To query the content items, we will use the ContentRetriever, as we did in the earlier step. This time we'll use it's RetrieveContent method to get the whole menu structure, including it's items, based on a provided menu codename.

C#NavigationMenuViewComponent.cs
using System.Linq;
using System.Threading.Tasks;
using Kentico.Content.Web.Mvc;
using Microsoft.AspNetCore.Mvc;

namespace Kickstart.Web.Features.Navigation;

public class NavigationMenuViewComponent : ViewComponent
{
    private readonly IContentRetriever contentRetriever;
    // inject the Xperience by Kentico Content retriever service
    public NavigationMenuViewComponent(IContentRetriever contentRetriever) => this.contentRetriever = contentRetriever;

    public async Task<IViewComponentResult> InvokeAsync(string navigationMenuCodeName)
    {
        var menu = await RetrieveMenu(navigationMenuCodeName);

        if (menu == null)
        {
            return View("~/Features/Navigation/NavigationMenuViewComponent.cshtml", new NavigationMenuViewModel());
        }

        var model = NavigationMenuViewModel.GetViewModel(menu);

        return View("~/Features/Navigation/NavigationMenuViewComponent.cshtml", model);
    }

    // new private method
    private async Task<NavigationMenu> RetrieveMenu(string navigationMenuCodeName)
    {
        // specify the depth of the content item tree we want to get (we need the menu and it's children items) 
        var parameters = new RetrieveContentParameters
        {
            LinkedItemsMaxLevel = 2
        };
        // retrieve the navigation menu object
        var menus = await contentRetriever.RetrieveContent<NavigationMenu>(
            parameters,
            query => query
                .Where(where => where.WhereEquals(nameof(NavigationMenu.NavigationMenuCodeName), navigationMenuCodeName)),
            RetrievalCacheSettings.CacheDisabled
            );

        return menus.FirstOrDefault();
    }
}

Design the component's view

Now, we just need a view to render the model provided by NavigationMenuViewComponent.

Add a file called NavigationMenuViewComponent.cshtml that renders a nav element containing a list of links from the model's Items.

Spice up the design of the menu with some Bootstrap CSS classes.

cshtmlNavigationMenuViewComponent.cshtml
@using Kickstart.Web.Features.Navigation

@model NavigationMenuViewModel

@if (Model?.Items?.Any() ?? false)
{
    <nav>
        <ul class="navbar-nav nav-underline">
            @foreach (var item in Model.Items)
            {
                <li class="nav-item">
                    <a href="@item.Url" class="nav-link link-light">@item.Title</a>
                </li>
            }
        </ul>
    </nav>
}

Finally, add the view component to the _Layout.cshtml view beneath the site heading, specifying MainNavigation as the codename of the menu to retrieve. Adjust the Bootstrap CSS classes as necessary.

cshtml_Layout.cshtml
...
<div class="navbar navbar-primary navbar-expand-sm navbar-dark bg-primary p-3">
    <div class="navbar-brand">
        <h2>Xperience Kickstart</h2>
    </div>
    <vc:navigation-menu navigation-menu-code-name="MainNavigation" />
</div>
...

With all these elements in place, you should have a working navigation menu that looks something like this:

If you haven't already, check out our Kickstart repository⁠ (opens in a new tab) to see the complete implementation of the website we created together in this series. To run the project, follow the README instructions.

You have built this site by hand, step by step. Continue to the next step to see what it takes to build the same site with an AI coding assistant.

← Previous page

12 of 14

Mark complete and continue →