Managing navigation menus

Hierarchies have many uses when it comes to structuring your content, such as navigation menus, related products, and collections. This overview explains 3 different ways to manage hierarchies in your Kentico Cloud projects and goes through the proper use cases for each. There is no one best way to build your hierarchies 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 contributors. To help illustrate the use cases, we will be working with navigation menus.

Table of contents

    A) Hard-coded navigation

    In this method, no part of your navigation is modeled in Kentico Cloud, it's all in the code. For an example, see our sample application.

    The Dancing Goat sample app with hard-coded navigation.

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

    B) Implicitly generated navigation (a hybrid approach)

    In this approach, part of your navigation is hard-coded, and another part is generated based on the content of your project. You are not explicitly modeling your menu inside Kentico Cloud.

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

    Here is a simple example: Your app has a hardcoded menu containing an "Articles" menu item. The subitems of "Articles" could be generated based on content items of the Article content type in your project.

    • Which brewing fits you?
    • On Roasts
    • Origins of Arabica Bourbon

    Here is how to do it, assuming your project already contains a few articles:

    1. Retrieve the names and URL slugs of all articles

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

    • Java
    import com.kenticocloud.delivery_core.*; import com.kenticocloud.delivery_rx.*; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; // Prepares an array to hold strongly-typed models List<TypeResolver<?>> typeResolvers = new ArrayList<>(); // Registers the type resolver for articles typeResolvers.add(new TypeResolver<>(Article.TYPE, new Function<Void, Article>() { @Override public Article apply(Void input) { return new Article(); } })); // Prepares the DeliveryService configuration object String projectId = "975bf280-fd91-488c-994c-2f04416e5ee3"; IDeliveryConfig config = DeliveryConfig.newConfig(projectId) .withTypeResolvers(typeResolvers); // Initializes a DeliveryService for Java projects IDeliveryService deliveryService = new DeliveryService(config); // Gets specific elements of all articles using a simple request List<Article> articles = deliveryService.<Article>items() .equalsFilter("system.type", "article") .elementsParameter(Arrays.asList("title", "url_pattern")) .get() .getItems(); // Gets specific elements of all articles using JavaRx2 deliveryService.<Article>items() .equalsFilter("system.type", "article") .elementsParameter(Arrays.asList("title", "url_pattern")) .getObservable() .subscribe(new Observer<DeliveryItemListingResponse<Article>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DeliveryItemResponse<ContentItem> response) { // Gets the content items Article item = response.getItem(); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    import com.kenticocloud.delivery_core.*; import com.kenticocloud.delivery_rx.*; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; // Prepares an array to hold strongly-typed models List<TypeResolver<?>> typeResolvers = new ArrayList<>(); // Registers the type resolver for articles typeResolvers.add(new TypeResolver<>(Article.TYPE, new Function<Void, Article>() { @Override public Article apply(Void input) { return new Article(); } })); // Prepares the DeliveryService configuration object String projectId = "975bf280-fd91-488c-994c-2f04416e5ee3"; IDeliveryConfig config = DeliveryConfig.newConfig(projectId) .withTypeResolvers(typeResolvers); // Initializes a DeliveryService for Java projects IDeliveryService deliveryService = new DeliveryService(config); // Gets specific elements of all articles using a simple request List<Article> articles = deliveryService.<Article>items() .equalsFilter("system.type", "article") .elementsParameter(Arrays.asList("title", "url_pattern")) .get() .getItems(); // Gets specific elements of all articles using JavaRx2 deliveryService.<Article>items() .equalsFilter("system.type", "article") .elementsParameter(Arrays.asList("title", "url_pattern")) .getObservable() .subscribe(new Observer<DeliveryItemListingResponse<Article>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DeliveryItemResponse<ContentItem> response) { // Gets the content items Article item = response.getItem(); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    • Swift
    import KenticoCloud let client = DeliveryClient.init(projectId:"975bf280-fd91-488c-994c-2f04416e5ee3") let customQuery = "items?system.type=article&elements=title,url_pattern" 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) } }
    import KenticoCloud let client = DeliveryClient.init(projectId:"975bf280-fd91-488c-994c-2f04416e5ee3") let customQuery = "items?system.type=article&elements=title,url_pattern" 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
    import com.kenticocloud.delivery; DeliveryClient client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3"); List<NameValuePair> params = DeliveryParameterBuilder.params() .filterEquals("system.type", "article") .projection("title", "url_pattern").build(); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models List<ArticleItem> items = client.getItems(ArticleItem.class, params);
    import com.kenticocloud.delivery; DeliveryClient client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3"); List<NameValuePair> params = DeliveryParameterBuilder.params() .filterEquals("system.type", "article") .projection("title", "url_pattern").build(); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models List<ArticleItem> items = client.getItems(ArticleItem.class, params);
    • JavaScript
    const KenticoCloud = require("kentico-cloud-delivery"); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models class Article extends KenticoCloud.ContentItem { constructor() { super(); } } const deliveryClient = new KenticoCloud.DeliveryClient({ projectId: "975bf280-fd91-488c-994c-2f04416e5ee3", typeResolvers: [ new KenticoCloud.TypeResolver("article", () => new Article()) ] }); deliveryClient.items() .type("article") .elementsParameter(["title", "url_pattern"]) .getObservable() .subscribe(response => console.log(response));
    const KenticoCloud = require("kentico-cloud-delivery"); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models class Article extends KenticoCloud.ContentItem { constructor() { super(); } } const deliveryClient = new KenticoCloud.DeliveryClient({ projectId: "975bf280-fd91-488c-994c-2f04416e5ee3", typeResolvers: [ new KenticoCloud.TypeResolver("article", () => new Article()) ] }); deliveryClient.items() .type("article") .elementsParameter(["title", "url_pattern"]) .getObservable() .subscribe(response => console.log(response));
    • C#
    using KenticoCloud.Delivery; // Initializes a content delivery client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3") .Build(); // Gets specific elements of all articles // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models DeliveryItemListingResponse<Article> response = await client.GetItemsAsync<Article>( new EqualsFilter("system.type", "article"), new ElementsParameter("title", "url_pattern") ); var items = response.Items;
    using KenticoCloud.Delivery; // Initializes a content delivery client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3") .Build(); // Gets specific elements of all articles // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models DeliveryItemListingResponse<Article> response = await client.GetItemsAsync<Article>( new EqualsFilter("system.type", "article"), new ElementsParameter("title", "url_pattern") ); var items = response.Items;
    • PHP
    <?php // Defined by Composer to include required libraries require __DIR__ . "/vendor/autoload.php"; use KenticoCloud\Delivery\DeliveryClient; use KenticoCloud\Delivery\QueryParams; $client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3"); $items = $client->getItems((new QueryParams()) ->equals("system.type", "article") ->elements(array("title", "url_pattern"));
    <?php // Defined by Composer to include required libraries require __DIR__ . "/vendor/autoload.php"; use KenticoCloud\Delivery\DeliveryClient; use KenticoCloud\Delivery\QueryParams; $client = new DeliveryClient("975bf280-fd91-488c-994c-2f04416e5ee3"); $items = $client->getItems((new QueryParams()) ->equals("system.type", "article") ->elements(array("title", "url_pattern"));
    • cURL
    curl --request GET \ --url "https://deliver.kenticocloud.com/975bf280-fd91-488c-994c-2f04416e5ee3/items?system.type=article&elements=title%2Curl_pattern" \ --header "content-type: application/json"
    curl --request GET \ --url "https://deliver.kenticocloud.com/975bf280-fd91-488c-994c-2f04416e5ee3/items?system.type=article&elements=title%2Curl_pattern" \ --header "content-type: application/json"
    • Ruby
    require "delivery-sdk-ruby" delivery_client = KenticoCloud::Delivery::DeliveryClient.new project_id: "975bf280-fd91-488c-994c-2f04416e5ee3" delivery_client.items("system.type".eq("article")) .elements(%w[title url_pattern]) .execute do |response| items = response.items end
    require "delivery-sdk-ruby" delivery_client = KenticoCloud::Delivery::DeliveryClient.new project_id: "975bf280-fd91-488c-994c-2f04416e5ee3" delivery_client.items("system.type".eq("article")) .elements(%w[title url_pattern]) .execute do |response| items = response.items end
    • TypeScript
    import { ContentItem, DeliveryClient, Fields, TypeResolver } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class Article extends ContentItem { public title: Fields.TextField; public summary: Fields.TextField; public post_date: Fields.DateTimeField; public teaser_image: Fields.AssetsField; public related_articles: Article[]; } const deliveryClient = new DeliveryClient({ projectId: "975bf280-fd91-488c-994c-2f04416e5ee3", typeResolvers: [ new TypeResolver("article", () => new Article) ] }); var articles: Article[]; deliveryClient.items<Article>() .type("article") .elementsParameter(["title", "url_pattern"]) .getObservable() .subscribe(response => { articles = response.items; console.log(articles); });
    import { ContentItem, DeliveryClient, Fields, TypeResolver } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class Article extends ContentItem { public title: Fields.TextField; public summary: Fields.TextField; public post_date: Fields.DateTimeField; public teaser_image: Fields.AssetsField; public related_articles: Article[]; } const deliveryClient = new DeliveryClient({ projectId: "975bf280-fd91-488c-994c-2f04416e5ee3", typeResolvers: [ new TypeResolver("article", () => new Article) ] }); var articles: Article[]; deliveryClient.items<Article>() .type("article") .elementsParameter(["title", "url_pattern"]) .getObservable() .subscribe(response => { articles = response.items; console.log(articles); });

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

    2. Display the retrieved data as links in your menu

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

    C) Explicitly modeled navigation

    In this method, your navigation is completely managed from Kentico Cloud using specialized content items. Each of them represents a menu item in your navigation and their relationships are defined using a Linked items element.

    This approach is the most flexible and empowers your content contributors to manage navigation menus without developer assistance. However, implementing it requires some setup in your project and inside your application. Here is a simple step-by-step example:

    1. Create a Navigation item content type

    To create a new content type, choose Content models in the app menu and then click Create new.

    How you might define your navigation structure with a Navigation item content type.

    Name your content type "Navigation item" in Content type name. Add at least these elements:

    • Title Text element.
    • Url URL Slug element.
    • Subitems Linked items element for holding the child menu items.

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

    2. Create navigation items

    To create new content items, choose Content & Assets in the app menu, then click Create new. One by one, add and publish the individual items in your menu. Use the Subitems Linked items element to link the items together into a content tree.

    A navigation item used to add "Articles" to the menu.

    3. Define first-level navigation items

    Choose which navigation items should be displayed at the first level of your menu and group them together inside another item:

    1. Create one more Navigation item.
    2. Type ROOT Navigation item in Content item name.
    3. Populate its Subitems element with your first-level navigation items.

    This root item holds all first-level menu items in its Linked items element.

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

    Note: Only the Subitems element is important here, the Title and Url values are irrelevant. You don't necessarily have to use a Navigation item. You can use any item of any content type that has a Linked items element to hold your navigation items.

    4. Retrieve your navigation items via the Delivery API

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

    • Java
    import com.kenticocloud.delivery_core.*; import com.kenticocloud.delivery_rx.*; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; // Prepares an array to hold strongly-typed models List<TypeResolver<?>> typeResolvers = new ArrayList<>(); // Registers the type resolver for articles typeResolvers.add(new TypeResolver<>(NavigationItem.TYPE, new Function<Void, NavigationItem>() { @Override public NavigationItem apply(Void input) { return new NavigationItem(); } })); // Prepares the DeliveryService configuration object String projectId = "24246f25-946d-4ab4-b170-fadef22fe7b2"; IDeliveryConfig config = DeliveryConfig.newConfig(projectId) .withTypeResolvers(typeResolvers); // Initializes a DeliveryService for Java projects IDeliveryService deliveryService = new DeliveryService(config); // Gets navigation items and their linked items using a simple request NavigationItem root = deliveryService.<NavigationItem>item("root_navigation_item") .depthParameter(5) .get() .getItem(); // Gets navigation items and their linked items using RxJava2 deliveryService.<NavigationItem>item("root_navigation_item") .depthParameter(5) .getObservable() .subscribe(new Observer<DeliveryItemResponse<NavigationItem>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DeliveryItemResponse<NavigationItem> response) { // Gets the item NavigationItem item = response.getItem(); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    import com.kenticocloud.delivery_core.*; import com.kenticocloud.delivery_rx.*; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Function; // Prepares an array to hold strongly-typed models List<TypeResolver<?>> typeResolvers = new ArrayList<>(); // Registers the type resolver for articles typeResolvers.add(new TypeResolver<>(NavigationItem.TYPE, new Function<Void, NavigationItem>() { @Override public NavigationItem apply(Void input) { return new NavigationItem(); } })); // Prepares the DeliveryService configuration object String projectId = "24246f25-946d-4ab4-b170-fadef22fe7b2"; IDeliveryConfig config = DeliveryConfig.newConfig(projectId) .withTypeResolvers(typeResolvers); // Initializes a DeliveryService for Java projects IDeliveryService deliveryService = new DeliveryService(config); // Gets navigation items and their linked items using a simple request NavigationItem root = deliveryService.<NavigationItem>item("root_navigation_item") .depthParameter(5) .get() .getItem(); // Gets navigation items and their linked items using RxJava2 deliveryService.<NavigationItem>item("root_navigation_item") .depthParameter(5) .getObservable() .subscribe(new Observer<DeliveryItemResponse<NavigationItem>>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(DeliveryItemResponse<NavigationItem> response) { // Gets the item NavigationItem item = response.getItem(); } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
    • Swift
    import KenticoCloud let client = DeliveryClient.init(projectId: "24246f25-946d-4ab4-b170-fadef22fe7b2") 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) } }
    import KenticoCloud let client = DeliveryClient.init(projectId: "24246f25-946d-4ab4-b170-fadef22fe7b2") 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
    import com.kenticocloud.delivery; DeliveryClient client = new DeliveryClient("24246f25-946d-4ab4-b170-fadef22fe7b2"); List<NameValuePair> params = DeliveryParameterBuilder.params().modularContentDepth(5).build(); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models ArticleItem item = client.getItem("root_navigation_item", NavigationItemItem.class, params);
    import com.kenticocloud.delivery; DeliveryClient client = new DeliveryClient("24246f25-946d-4ab4-b170-fadef22fe7b2"); List<NameValuePair> params = DeliveryParameterBuilder.params().modularContentDepth(5).build(); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models ArticleItem item = client.getItem("root_navigation_item", NavigationItemItem.class, params);
    • JavaScript
    const KenticoCloud = require("kentico-cloud-delivery"); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models class NavigationItem extends KenticoCloud.ContentItem { constructor() { super(); } } const deliveryClient = new KenticoCloud.DeliveryClient({ projectId: "24246f25-946d-4ab4-b170-fadef22fe7b2", typeResolvers: [ new KenticoCloud.TypeResolver("navigation_item", () => new NavigationItem) ] }); deliveryClient.item("root_navigation_item") .depthParameter(5) .getObservable() .subscribe(response => console.log(response.item));
    const KenticoCloud = require("kentico-cloud-delivery"); // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models class NavigationItem extends KenticoCloud.ContentItem { constructor() { super(); } } const deliveryClient = new KenticoCloud.DeliveryClient({ projectId: "24246f25-946d-4ab4-b170-fadef22fe7b2", typeResolvers: [ new KenticoCloud.TypeResolver("navigation_item", () => new NavigationItem) ] }); deliveryClient.item("root_navigation_item") .depthParameter(5) .getObservable() .subscribe(response => console.log(response.item));
    • C#
    using KenticoCloud.Delivery; // Initializes a content delivery client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3") .Build(); // Gets navigation items and their linked items // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models DeliveryItemResponse<NavigationItem> response = await client.GetItemAsync<NavigationItem>("root_navigation_item", new DepthParameter(5) ); var item = response.Item;
    using KenticoCloud.Delivery; // Initializes a content delivery client IDeliveryClient client = DeliveryClientBuilder .WithProjectId("975bf280-fd91-488c-994c-2f04416e5ee3") .Build(); // Gets navigation items and their linked items // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models DeliveryItemResponse<NavigationItem> response = await client.GetItemAsync<NavigationItem>("root_navigation_item", new DepthParameter(5) ); var item = response.Item;
    • PHP
    <?php // Defined by Composer to include required libraries require __DIR__ . "/vendor/autoload.php"; use KenticoCloud\Delivery\DeliveryClient; $client = new DeliveryClient("24246f25-946d-4ab4-b170-fadef22fe7b2"); $item = $client->getItem("root_navigation_item", (new QueryParams()) ->depth(5));
    <?php // Defined by Composer to include required libraries require __DIR__ . "/vendor/autoload.php"; use KenticoCloud\Delivery\DeliveryClient; $client = new DeliveryClient("24246f25-946d-4ab4-b170-fadef22fe7b2"); $item = $client->getItem("root_navigation_item", (new QueryParams()) ->depth(5));
    • cURL
    curl --request GET \ --url "https://deliver.kenticocloud.com/24246f25-946d-4ab4-b170-fadef22fe7b2/items/root_navigation_item?depth=5" \ --header "content-type: application/json"
    curl --request GET \ --url "https://deliver.kenticocloud.com/24246f25-946d-4ab4-b170-fadef22fe7b2/items/root_navigation_item?depth=5" \ --header "content-type: application/json"
    • Ruby
    require "delivery-sdk-ruby" delivery_client = KenticoCloud::Delivery::DeliveryClient.new project_id: "975bf280-fd91-488c-994c-2f04416e5ee3" delivery_client.item("root_navigation_item") .depth(5) .execute do |response| item = response.item end
    require "delivery-sdk-ruby" delivery_client = KenticoCloud::Delivery::DeliveryClient.new project_id: "975bf280-fd91-488c-994c-2f04416e5ee3" delivery_client.item("root_navigation_item") .depth(5) .execute do |response| item = response.item end
    • TypeScript
    import { DeliveryClient, TypeResolver, ContentItem, Fields } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class NavigationItem extends ContentItem { public title: Fields.TextField; public url_slug: Fields.UrlSlugField; public subitems: NavigationItem[]; } const deliveryClient = new DeliveryClient({ projectId: "24246f25-946d-4ab4-b170-fadef22fe7b2", typeResolvers: [ new TypeResolver("navigation_item", () => new NavigationItem) ] }); var root: NavigationItem; deliveryClient.item<NavigationItem>("root_navigation_item") .depthParameter(5) .getObservable() .subscribe(response => { root = response.item; console.log(root); });
    import { DeliveryClient, TypeResolver, ContentItem, Fields } from "kentico-cloud-delivery"; // Create strongly typed models according to https://developer.kenticocloud.com/docs/strongly-typed-models export class NavigationItem extends ContentItem { public title: Fields.TextField; public url_slug: Fields.UrlSlugField; public subitems: NavigationItem[]; } const deliveryClient = new DeliveryClient({ projectId: "24246f25-946d-4ab4-b170-fadef22fe7b2", typeResolvers: [ new TypeResolver("navigation_item", () => new NavigationItem) ] }); var root: NavigationItem; deliveryClient.item<NavigationItem>("root_navigation_item") .depthParameter(5) .getObservable() .subscribe(response => { root = response.item; console.log(root); });

    5. Generate a menu

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

    Note: Please notice that the content of the linked items is inside the modular_content property of the response (historical reasons).

    Voila! You now have a dynamic menu that you can manage entirely from Kentico Cloud. You can find a more complex example of this approach on our blog:

    Worked example 

    Check out Managing Navigation Menus in Kentico Cloud for an in-depth guide to managing your navigation and rendering it in an MVC.NET application.

    (Optional) Create sitemap.xml

    Similarly to generating a dynamic menu for your application, you can use the modeled navigation in Kentico Cloud to generate a sitemap.xml file for your website.

    Summary

    In this tutorial, you have learned about three different ways to manage navigation in your Kentico Cloud projects. You can pick the one that best suits your priorities, such as the speed of initial development (approach A), low maintenance cost (approach B), or flexibility and empowerment of your content contributors (approach C). 

    Each approach is a trade-off, but you can also combine them together and manage each part of your navigation separately, in the most efficient way possible. 

    What's next?

    • 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 Kentico Cloud.
    • Have a look at our development resources for your technology.