Skip navigation

Manage navigation menus

Hierarchies have many uses when it comes to structuring your content, such as navigation menus or related products. This overview explains 3 different ways to manage hierarchies in your Kontent projects and goes through the proper use cases for each. 

Each approach has its pros and cons and you should always consider the particular requirements of your project. Generally, your approach should reflect how much control you want to give to your content creators.

Table of contents

    Navigation modeled in Kontent

    The most flexible method is to manage your navigation completely in Kontent using dedicated content items. These items represent menu items in your navigation and you'll define their relationships by linking them together using a linked items element.

    This approach enables your content creators to manage navigation menus without developer assistance. Let's see how to tackle it step by step.

    Navigation in Web Spotlight

    When using Web Spotlight to manage your website content, define the relationships by using a subpages element.

    1. Create a content type for navigation items

    In Content model, click Create new to create a new content type and name it Navigation item.

    Add at least these elements:

    • Text element – name it Title
    • URL slug element – name it URL
    • Linked items element for child menu items – name it Subitems
    Content type of a navigation item with Title, URL, and Subitems element.

    Define your navigation structure with a Navigation item content type.

    You can add more elements depending on your needs, such as a redirect URL.

    2. Define your top-level navigation items

    The first thing to do is to create a wrapper for your top-level navigation items. You need this to know which navigation items are highest in the hierarchy when you generate the menu in the next steps.

    In  Content & assets, create a new content item of the Navigation item type and name it Root navigation item

    Now that you have the wrapper, add the top-level navigation items to its Subitems element. You can add content items with actual content, such as Home or About us, or items of the Navigation type if you want to create a sub-menu. We'll get into more detail about sub-menus in the next step.

    Showing hierarchy created using navigation items.

    The root item holds all first-level menu items in its linked items element called Subitems.

    3. Create lower levels in the navigation

    To create sub-menus, use items of the Navigation item type and their Subitems element. The principle is the same as with the Root navigation item

    Open a navigation item under which you want to display a sub-menu and add the items of the sub-menu to the Subitems element.

    Articles navigation item with two articles and a child item with two more articles in it.

    A navigation item used to list articles hierarchy.

    You can create lower sub-menus in your navigation by nesting content items of the Navigation item type. Have a look at the Insurance content item in the image above to see an example.

    When you're done, publish the Root navigation item together with all its linked items. 

    In the following step, you will request the root item via the Delivery API to generate your menu.

    4. Retrieve your navigation items

    When requesting the Root navigation item via the Delivery API, specify the depth parameter to retrieve more than one level of linked items.

    • Swift
    // Tip: Find more about Swift SDK at https://docs.kontent.ai/ios import KenticoKontentDelivery let client = DeliveryClient.init(projectId: "8d20758c-d74c-4f59-ae04-ee928c0816b7") let customQuery = "items/root_navigation_item?depth=5" client.getItem(modelType: NavigationItem.self, customQuery: customQuery) { (isSuccess, deliveryItem, error) in if isSuccess { if let root = deliveryItem.item { // Use your item here } } else { if let error = error { print(error) } }
    // Tip: Find more about Swift SDK at https://docs.kontent.ai/ios import KenticoKontentDelivery let client = DeliveryClient.init(projectId: "8d20758c-d74c-4f59-ae04-ee928c0816b7") let customQuery = "items/root_navigation_item?depth=5" client.getItem(modelType: NavigationItem.self, customQuery: customQuery) { (isSuccess, deliveryItem, error) in if isSuccess { if let root = deliveryItem.item { // Use your item here } } else { if let error = error { print(error) } }
    • Java
    // Tip: Find more about Java SDK at https://docs.kontent.ai/java import kentico.kontent.delivery.*; // Initializes a DeliveryClient DeliveryClient client = new DeliveryClient("<YOUR_PROJECT_ID>"); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models // Registers the model class for navigation items client.registerType(NavigationItem.class); // Gets navigation items and their linked items CompletionStage<NavigationItem> root = client.getItem( "root_navigation_item", NavigationItem.class, DeliveryParameterBuilder.params() .linkedItemsDepth(5) .build() ); // To use the code for Android projects, see http://docs.kontent.ai/android
    // Tip: Find more about Java SDK at https://docs.kontent.ai/java import kentico.kontent.delivery.*; // Initializes a DeliveryClient DeliveryClient client = new DeliveryClient("<YOUR_PROJECT_ID>"); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models // Registers the model class for navigation items client.registerType(NavigationItem.class); // Gets navigation items and their linked items CompletionStage<NavigationItem> root = client.getItem( "root_navigation_item", NavigationItem.class, DeliveryParameterBuilder.params() .linkedItemsDepth(5) .build() ); // To use the code for Android projects, see http://docs.kontent.ai/android
    • JavaScript
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript const KontentDelivery = require('@kentico/kontent-delivery'); const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new KontentDelivery.TypeResolver('navigation_item', (rawData) => new NavigationItem) ] }); deliveryClient.item('root_navigation_item') .depthParameter(5) .toObservable() .subscribe(response => console.log(response.item));
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript const KontentDelivery = require('@kentico/kontent-delivery'); const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new KontentDelivery.TypeResolver('navigation_item', (rawData) => new NavigationItem) ] }); deliveryClient.item('root_navigation_item') .depthParameter(5) .toObservable() .subscribe(response => console.log(response.item));
    • C#
    // Tip: Find more about .NET SDKs at https://docs.kontent.ai/net using Kentico.Kontent.Delivery; // Creates an instance of the delivery client // ProTip: Use DI for this in your apps https://docs.kontent.ai/net-register-client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("8d20758c-d74c-4f59-ae04-ee928c0816b7") .Build(); // Gets navigation items and their linked items // Create strongly typed models according to https://docs.kontent.ai/net-strong-types IDeliveryItemResponse<NavigationItem> response = await client.GetItemAsync<NavigationItem>("root_navigation_item", new DepthParameter(5) ); NavigationItem item = response.Item;
    // Tip: Find more about .NET SDKs at https://docs.kontent.ai/net using Kentico.Kontent.Delivery; // Creates an instance of the delivery client // ProTip: Use DI for this in your apps https://docs.kontent.ai/net-register-client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("8d20758c-d74c-4f59-ae04-ee928c0816b7") .Build(); // Gets navigation items and their linked items // Create strongly typed models according to https://docs.kontent.ai/net-strong-types IDeliveryItemResponse<NavigationItem> response = await client.GetItemAsync<NavigationItem>("root_navigation_item", new DepthParameter(5) ); NavigationItem item = response.Item;
    • PHP
    // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php // Defined by Composer to include required libraries require __DIR__ . '/vendor/autoload.php'; use Kentico\Kontent\Delivery\DeliveryClient; $client = new DeliveryClient('8d20758c-d74c-4f59-ae04-ee928c0816b7'); $item = $client->getItem('root_navigation_item', (new QueryParams()) ->depth(5));
    // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php // Defined by Composer to include required libraries require __DIR__ . '/vendor/autoload.php'; use Kentico\Kontent\Delivery\DeliveryClient; $client = new DeliveryClient('8d20758c-d74c-4f59-ae04-ee928c0816b7'); $item = $client->getItem('root_navigation_item', (new QueryParams()) ->depth(5));
    • cURL
    curl --request GET \ --url 'https://deliver.kontent.ai/8d20758c-d74c-4f59-ae04-ee928c0816b7/items/root_navigation_item?depth=5' \ --header 'content-type: application/json'
    curl --request GET \ --url 'https://deliver.kontent.ai/8d20758c-d74c-4f59-ae04-ee928c0816b7/items/root_navigation_item?depth=5' \ --header 'content-type: application/json'
    • Ruby
    # Tip: Find more about Ruby SDKs at https://docs.kontent.ai/ruby require 'delivery-sdk-ruby' delivery_client = Kentico::Kontent::Delivery::DeliveryClient.new project_id: '8d20758c-d74c-4f59-ae04-ee928c0816b7' delivery_client.item('root_navigation_item') .depth(5) .execute do |response| item = response.item end
    # Tip: Find more about Ruby SDKs at https://docs.kontent.ai/ruby require 'delivery-sdk-ruby' delivery_client = Kentico::Kontent::Delivery::DeliveryClient.new project_id: '8d20758c-d74c-4f59-ae04-ee928c0816b7' delivery_client.item('root_navigation_item') .depth(5) .execute do |response| item = response.item end
    • TypeScript
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript import { DeliveryClient, TypeResolver, ContentItem, Elements } from '@kentico/kontent-delivery'; import { NavigationItem } from './models/NavigationItem'; const deliveryClient = new DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new TypeResolver('navigation_item', (rawData) => new NavigationItem) ] }); let root: NavigationItem; deliveryClient.item<NavigationItem>('root_navigation_item') .depthParameter(5) .toObservable() .subscribe(response => { root = response.item; console.log(root); });
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript import { DeliveryClient, TypeResolver, ContentItem, Elements } from '@kentico/kontent-delivery'; import { NavigationItem } from './models/NavigationItem'; const deliveryClient = new DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new TypeResolver('navigation_item', (rawData) => new NavigationItem) ] }); let root: NavigationItem; deliveryClient.item<NavigationItem>('root_navigation_item') .depthParameter(5) .toObservable() .subscribe(response => { root = response.item; console.log(root); });

    5. Generate a menu

    Use the retrieved data to construct a nested list of links:

    The content of the linked items is inside the modular_content property of the response.

    And that's it! You now have a dynamic menu that you can manage entirely from Kontent.

    Worked example 

    You can find a more complex example of this approach on our blog. Check out Managing Navigation Menus in Kontent for an in-depth guide to managing your navigation and rendering it in an MVC.NET application.

    (Optional) Create a sitemap for your website

    Similarly to generating a dynamic menu for your application, you can use the navigation modeled in Kontent to generate a sitemap for your website. Sitemaps can also be modeled using taxonomy.

    Other options

    If you don't want to model your navigation entirely in Kontent, there are other approaches that involve more or less hardcoding your navigation into your website or app.

    Hybrid navigation

    When you create hybrid navigation, the static part of your navigation is hardcoded and the dynamic part is generated based on the content of your project. You are not explicitly modeling your menu inside Kontent.

    This approach is suitable for projects where the basic structure of your navigation is static, but certain parts of navigation change as new content is added.

    Here's a simple example: Your app has a hardcoded menu containing an Articles menu item. The subitems of Articles are generated dynamically based on content items of the Article content type in your project.

    Let's see how this can be done.

    1. Retrieve the titles and URL slugs of your articles

    Use the projection capabilities of the Delivery API to retrieve only titles and URLs of your articles.

    • Swift
    // Tip: Find more about Swift SDK at https://docs.kontent.ai/ios import KenticoKontentDelivery let client = DeliveryClient.init(projectId:"8d20758c-d74c-4f59-ae04-ee928c0816b7") let customQuery = "items?system.type=article&elements=title,url" client.getItems(modelType: Article.self, customQuery: customQuery) { (isSuccess, itemsResponse, error) in if isSuccess { if let articles = itemsResponse?.items { // Use your items here } } else { if let error = error { print(error) } }
    // Tip: Find more about Swift SDK at https://docs.kontent.ai/ios import KenticoKontentDelivery let client = DeliveryClient.init(projectId:"8d20758c-d74c-4f59-ae04-ee928c0816b7") let customQuery = "items?system.type=article&elements=title,url" client.getItems(modelType: Article.self, customQuery: customQuery) { (isSuccess, itemsResponse, error) in if isSuccess { if let articles = itemsResponse?.items { // Use your items here } } else { if let error = error { print(error) } }
    • Java
    // Tip: Find more about Java SDK at https://docs.kontent.ai/java import kentico.kontent.delivery.*; // Initializes a DeliveryClient DeliveryClient client = new DeliveryClient("<YOUR_PROJECT_ID>"); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models // Registers the model class for articles client.registerType(Article.class); // Gets specific elements of all articles using a simple request CompletionStage<List<Article>> articles = client.getItems( Article.class, DeliveryParameterBuilder.params() .projection("title", "url") .build() ); // To use the code for Android projects, see http://docs.kontent.ai/android
    // Tip: Find more about Java SDK at https://docs.kontent.ai/java import kentico.kontent.delivery.*; // Initializes a DeliveryClient DeliveryClient client = new DeliveryClient("<YOUR_PROJECT_ID>"); // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models // Registers the model class for articles client.registerType(Article.class); // Gets specific elements of all articles using a simple request CompletionStage<List<Article>> articles = client.getItems( Article.class, DeliveryParameterBuilder.params() .projection("title", "url") .build() ); // To use the code for Android projects, see http://docs.kontent.ai/android
    • JavaScript
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript const KontentDelivery = require('@kentico/kontent-delivery'); const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new KontentDelivery.TypeResolver('article', (rawData) => new Article()) ] }); deliveryClient.items() .type('article') .elementsParameter(['title', 'url']) .toObservable() .subscribe(response => console.log(response));
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript const KontentDelivery = require('@kentico/kontent-delivery'); const deliveryClient = new KontentDelivery.DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new KontentDelivery.TypeResolver('article', (rawData) => new Article()) ] }); deliveryClient.items() .type('article') .elementsParameter(['title', 'url']) .toObservable() .subscribe(response => console.log(response));
    • C#
    // Tip: Find more about .NET SDKs at https://docs.kontent.ai/net using Kentico.Kontent.Delivery; // Creates an instance of the delivery client // ProTip: Use DI for this in your apps https://docs.kontent.ai/net-register-client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("8d20758c-d74c-4f59-ae04-ee928c0816b7") .Build(); // Gets specific elements of all articles // Create strongly typed models according to https://docs.kontent.ai/net-strong-types IDeliveryItemListingResponse<Article> response = await client.GetItemsAsync<Article>( new EqualsFilter("system.type", "article"), new ElementsParameter("title", "url") ); var items = response.Items;
    // Tip: Find more about .NET SDKs at https://docs.kontent.ai/net using Kentico.Kontent.Delivery; // Creates an instance of the delivery client // ProTip: Use DI for this in your apps https://docs.kontent.ai/net-register-client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("8d20758c-d74c-4f59-ae04-ee928c0816b7") .Build(); // Gets specific elements of all articles // Create strongly typed models according to https://docs.kontent.ai/net-strong-types IDeliveryItemListingResponse<Article> response = await client.GetItemsAsync<Article>( new EqualsFilter("system.type", "article"), new ElementsParameter("title", "url") ); var items = response.Items;
    • PHP
    // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php // Defined by Composer to include required libraries require __DIR__ . '/vendor/autoload.php'; use Kentico\Kontent\Delivery\DeliveryClient; use Kentico\Kontent\Delivery\QueryParams; $client = new DeliveryClient('8d20758c-d74c-4f59-ae04-ee928c0816b7'); $items = $client->getItems((new QueryParams()) ->equals('system.type', 'article') ->elements(array("title", "url")));
    // Tip: Find more about PHP SDKs at https://docs.kontent.ai/php // Defined by Composer to include required libraries require __DIR__ . '/vendor/autoload.php'; use Kentico\Kontent\Delivery\DeliveryClient; use Kentico\Kontent\Delivery\QueryParams; $client = new DeliveryClient('8d20758c-d74c-4f59-ae04-ee928c0816b7'); $items = $client->getItems((new QueryParams()) ->equals('system.type', 'article') ->elements(array("title", "url")));
    • cURL
    curl --request GET \ --url 'https://deliver.kontent.ai/8d20758c-d74c-4f59-ae04-ee928c0816b7/items?system.type=article&elements=title%2Curl' \ --header 'content-type: application/json'
    curl --request GET \ --url 'https://deliver.kontent.ai/8d20758c-d74c-4f59-ae04-ee928c0816b7/items?system.type=article&elements=title%2Curl' \ --header 'content-type: application/json'
    • Ruby
    # Tip: Find more about Ruby SDKs at https://docs.kontent.ai/ruby require 'delivery-sdk-ruby' delivery_client = Kentico::Kontent::Delivery::DeliveryClient.new project_id: '8d20758c-d74c-4f59-ae04-ee928c0816b7' delivery_client.items('system.type'.eq('article')) .elements(%w[title url]) .execute do |response| items = response.items end
    # Tip: Find more about Ruby SDKs at https://docs.kontent.ai/ruby require 'delivery-sdk-ruby' delivery_client = Kentico::Kontent::Delivery::DeliveryClient.new project_id: '8d20758c-d74c-4f59-ae04-ee928c0816b7' delivery_client.items('system.type'.eq('article')) .elements(%w[title url]) .execute do |response| items = response.items end
    • TypeScript
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript import { DeliveryClient, TypeResolver } from '@kentico/kontent-delivery'; import { Article } from './models/Article'; const deliveryClient = new DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new TypeResolver('article', (rawData) => new Article) ] }); let articles: Article[]; deliveryClient.items<Article>() .type('article') .elementsParameter(['title', 'url']) .toObservable() .subscribe(response => { articles = response.items; console.log(articles); });
    // Tip: Find more about JS/TS SDKs at https://docs.kontent.ai/javascript import { DeliveryClient, TypeResolver } from '@kentico/kontent-delivery'; import { Article } from './models/Article'; const deliveryClient = new DeliveryClient({ projectId: '8d20758c-d74c-4f59-ae04-ee928c0816b7', typeResolvers: [ // Create strongly typed models according to https://docs.kontent.ai/strongly-typed-models new TypeResolver('article', (rawData) => new Article) ] }); let articles: Article[]; deliveryClient.items<Article>() .type('article') .elementsParameter(['title', 'url']) .toObservable() .subscribe(response => { articles = response.items; console.log(articles); });

    For taxonomy-driven navigation, you can retrieve items tagged with a specific taxonomy term.

    As your content creators write and publish more articles, your menu is updated automatically. They can't and don't have to change the navigation manually, which means less work and less potential for mistakes. However, structural changes have to be implemented by developers.

    Fully hardcoded navigation

    With hardcoded navigation, no part of your navigation is modeled in Kontent, it's all in the code. See our sample application as an example.

    A screenshot of the top navigation in our sample app.

    The Dancing Goat sample app with hardcoded navigation.

    This approach is suitable for small projects with static navigation where you don't want your content creators to make changes to the navigation. It's fast and simple at the beginning, but every subsequent change must be implemented by a developer.

    Summary

    In this tutorial, you have learned about three different ways to manage navigation in your Kontent projects. You can pick the one that best suits your priorities, such as the flexibility and empowerment of your content creators (navigation fully modeled in Kontent), speed of initial development (hardcoded navigation), or low maintenance cost (hybrid navigation).

    Each approach has a trade-off but you can also combine them together and manage each part of your navigation separately to achieve the most efficient way.

    What's next?

    • Create a sitemap for your website to improve SEO and accessibility.
    • Discuss your own approach to building navigation menus with our developer community on Stack Overflow.
    • Visit the development section of our blog for more inspiration and some advanced examples of using Kontent.
    • Have a look at our development resources for your technology.