Let’s walk through the steps to create a basic Declared Navigation rendering. We’ll tackle a simple Primary Navigation bar that would be included in all pages on a given site. Here’s an example of the output as it would appear using Bootstrap 4.x:

The markup looks like this:
<div class="navbar-nav mr-auto">
<a class="nav-item nav-link" href="/en/products">Products</a>
<a class="nav-item nav-link" href="/en/for-home">For Home </a>
<a class="nav-item nav-link" href="/en/for-business">For Business </a>
<a class="nav-item nav-link" href="/en/about-us">About Us </a>
<a class="nav-item nav-link" href="/en/news">News </a>
</div>
Prerequisites
Make sure you’ve installed the NuGet package, built and deployed your Sitecore project.
Within Sitecore, we’ll use Constellation.Feature.Navigation Items to create the Navigation Menu, with Navigation Links beneath it:

For instant gratification, make sure to Publish your Navigation Menu Items before deploying our code.
The Controller
One challenge for navigation with context highlighting is that the datasource Item that provides the details about the navigation cannot be used as a caching ID, since the navigation’s presentation will differ depending upon the page being rendered. Our controller addresses this by caching the calculated ViewModel based upon the ID of the Context Item.
using System;
using System.Web.Mvc;
using Constellation.Feature.Navigation.Models;
using Constellation.Feature.Navigation.Repositories;
using Constellation.Foundation.Caching;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Mvc.Presentation;
namespace Website.Areas.ExampleSite.Controllers.Navigation
{
public class ExampleNavigationController : Controller
{
public ExampleNavigationController(IDeclaredNavigationRepository repository, ICacheManager cache)
{
Repository = repository;
Cache = cache;
}
public IDeclaredNavigationRepository Repository { get; }
public ICacheManager Cache { get; }
public ActionResult Index()
{
Item datasource = RenderingContext.Current.ContextItem;
Item contextItem = RenderingContext.Current.PageContext.Item;
NavigationMenu model = GetModel(datasource, contextItem);
return View("~/Areas/ExampleSite/Views/Navigation/ExampleNavigation.cshtml", model);
}
private NavigationMenu GetModel(Item datasource, Item contextItem)
{
Assert.ArgumentNotNull(datasource, "datasource");
Assert.ArgumentNotNull(contextItem, "contextItem");
/* Rendering Datasource should be the root level Navigation Menu Item,
* in our case /ExampleSite/Navigation/Primary
* We want to know which menu item to highlight, so we also have to pass in the Context Item.
*
* We can't cache this Item by Datasource, because we'll lose context highlighting, but we don't
* want to generate this model from scratch for every request, so we're going to cache it by the
* contextItem's ID
*/
var key = this.GetType().FullName + contextItem.ID;
var model = Cache.Get<NavigationMenu>(key);
if (model == null)
{
model = Repository.GetNavigation(datasource, contextItem);
Cache.Add(key, model, DateTime.Now.AddMinutes(60));
}
return model;
}
}
}
ICacheManager above is provided by Constellation.Foundation.Caching.
The Repository
DeclaredNavigationRepository requires a Datasource Item that represents the root of a given Navigation Menu. If the rendered menu needs to support context highlighting, then the developer also needs to supply the Context Item.
It is expected that the Datasource Item inherits from the Navigation Menu template, and that its descendants inherit from either Link Group or Navigation Link Item templates. Anything else will be ignored. (There will be a warning in the Sitecore log file.)
For Context Highlighting, at least one of the Navigation Links in a given Navigation Menu branch must contain a Link field that points to a Sitecore Item that passes the ancestor-or-self axes test against the supplied Context Item.
The Model
IDeclaredNavigationRepository will always return a NavigationMenu object. The object can then be used to generate your menu’s markup.
Navigation Menu Properties
- DisplayName The DisplayName of the Navigation Menu Item
- ChildGroups A collection of LinkGroup objects that are based on Link Group Item children of the Navigation Menu Item.
- ChildLinks A collection of NavigationLink objects that are based on Navigation Link Item children of the Navigation Menu Item.
Link Group Properties
NavigationMenu is actually a specialization of LinkGroup, which is described below for completeness.
- DisplayName The DisplayName of the Link Group Item
- Parent The LinkGroup or NavigationMenu object that holds the current Item in one of its Child collections.
- ChildGroups A collection of LinkGroup objects that are based on Link Group Item children of the Link Group Item.
- ChildLinks A collection of NavigationLink objects that are based on Navigation Link Item children of the Link Group Item.
- IsActive True if a member of ChildGroups or ChildLinks is marked IsActive.
LinkGroup is used when your navigation menu system requires subdivisions in a given list. An example might be columns in a Mega Menu or Footer. LinkGroup objects are not links themselves, but they can hold further Groups, and they have a DisplayName property should the Group need some sort of visible title.
While it is possible to mix and match collections of Child Links and Child Groups, this strategy is not recommended, as it increases View logic complexity. Instead we recommend splitting your navigation menu into discrete components, one for the “plain” links and one for the grouping.
Navigation Link Members
The NavigationLink object represents a menu option that displays a link to another Internet resource. It can link to External resources as well as Sitecore pages and media library items. There are extensive options to allow the implementer to control the appearance of the menu option as well as the link behavior.
- Parent
The LinkGroup or NavigationMenu object that holds the current Item in one of its Child collections. - Link The URL this Menu Option is meant to present to the user.
- LinkClass the CSS Class for this Menu Option
- LinkTitle the value for the anchor tag’s title attribute
- LinkText the inner text of the anchor tag
- LinkTarget the value for the anchor tag’s target attribute
- LinkTargetItem the Sitecore Item represented by the Link property’s URL. Note that this may be null if the NavigationLink is pointing to an external URL.
- UseThisDisplayName if true, affects the output of GetBestLinkText()
- DisplayName The DisplayName of the NavigationLink Item
- ChildLinks a collection of NavigationLink objects that represent child Items of the current object’s source Item.
- IsActive True if the LinkTargetItem is an ancestor-or-self of the Context Item, or if any member of the ChildLinks collection is marked IsActive.
- GetBestLinkText() Returns a value to use for anchor tag inner text based on the order of the following evaluations:
- If UseThisDisplayName is true, returns DisplayName
- If LinkText is not null or empty, returns LinkText
- If LinkTargetItem is not null and LinkTargetItem has a usable Navigation Title field value, returns the Navigation Title field value.
- If the LinkTargetItem is not null, returns the LinkTargetItem’s DisplayName.
- Returns the current object’s DisplayName.
We strongly recommend using GetBestLinkText() whenever rendering the inner text of an anchor tag. This allows the content authors the most freedom to customize the appearance of a given menu options.
The View
While we’ve reviewed a number of rich objects above, and the controller is a bit more complex than other Constellation examples, the View is incredibly simple:
@model Constellation.Feature.Navigation.Models.NavigationMenu
<div class="navbar-nav mr-auto">
@foreach (var link in Model.ChildLinks)
{
if (link.IsActive)
{
<a class="nav-item nav-link active" href="@link.Link">@link.GetBestLinkText() <span class="sr-only">(current)</span></a>
}
else
{
<a class="nav-item nav-link" href="@link.Link">@link.GetBestLinkText() </a>
}
}
</div>
Here we’re using the three most accessed properties of NavigationLink: IsActive for context, Link for the URL, and GetBestLinkText() for the inner text of the anchor tag. We are intentionally ignoring LinkTarget because it is not best-practice to have a primary navigation link that opens in a new browser window.