---
title: "Walkthrough: Creating a model-bound view that renders a contents resolver"
slug: "walkthrough--creating-a-model-bound-view-that-renders-a-contents-resolver"
abstract: "How to create a Sitecore model-bound view that renders the output of a contents resolver."
---
# Walkthrough: Creating a model-bound view that renders a contents resolver

> [!important]
> If you integrated your app before , it's using the legacy ASP.NET Core Rendering SDK, version 22 or earlier. This SDK is no longer receiving updates, so we recommend that you [upgrade to the latest version of the new](https://doc.sitecore.com/xmc/en/developers/asp-net/latest/asp-net-core-sdk/upgrade-from-v22.html) .

You use contents resolvers with the [Sitecore Layout Service](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/sitecore-layout-service.html "Sitecore Layout Service") to provide more complex data beyond the serialization of a component data source. Common use cases include site navigation, search results, or returning the output or state of custom business logic. In this walkthrough, you learn how to create a contents resolver that returns a model of the content tree for you to render in a model-bound view.

When you have completed the walkthrough, we recommend you familiarize yourself with the [Sitecore Helix](https://helix.sitecore.net/) practices to organize your solution according to the business domain and create a common closure for related code from multiple services. This allows your related content resolvers and rendering code to be co-located within your solution. See the [Sitecore Helix Examples](https://github.com/Sitecore/Helix.Examples) repository for inspiration.

> [!note]
> This document assumes you are [using the Getting Started template](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/walkthrough--using-the-getting-started-template.html "Walkthrough: Using the Getting Started template") or have a working Sitecore solution with a `Platform` project for your Content Management and Content Delivery instance and a `RenderingHost` project.
>
> See [how to publish, run, and test the Getting Started template items and projects](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/publish,-run,-and-test-the-getting-started-template-projects.html "Publish, run, and test the Getting Started template projects").

To create a contents resolver, you must complete the following procedures:

- Add the Sitecore.LayoutService NuGet package.
- Add the Templates class.
- Create the contents resolver class.
- Create the contents resolver item.
- Create the JSON rendering item.
- Add the JSON rendering item to the standard values of the `AppRoute` template.
- Test the contents resolver.

To create a model-bound view that renders the contents resolver, you must complete the following procedures:

- Create the model.
- Create the model-bound view.
- Register the model-bound view.
- Test the model-bound view.

<details>
<summary><h2>Add the Sitecore.LayoutService NuGet package</h2></summary>

The `Sitecore.LayoutService` NuGet package contains the `Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver` class that the contents resolver class inherits from.

To add the `Sitecore.LayoutService` NuGet package to the `Platform` project:

1. In Visual Studio, in Solution Explorer, right-click `Platform/References` and click **Manage NuGet Packages**.
2. In the top-right corner of the window, in the **Package source** menu, select the package source that points to `https://nuget.sitecore.com/resources/v3/index.json`.
3. In the top-left corner of the window, click **Browse** and search for the `Sitecore.LayoutService` NuGet package.
4. In the list of search results, click `Sitecore.LayoutService`.
5. On the right side of the window, click **Install** to download and install the `Sitecore.LayoutService` NuGet package.
6. Allow the Nuget Package Manager to modify the solution if required.

</details>

<details>
<summary><h2>Add the Templates class</h2></summary>

The `Templates` class is a dependency of the contents resolver class.

To add the `Templates` class to the `Platform` project:

1. In Visual Studio, in Solution Explorer, right-click `Platform` and click **Add**, **New Item**.
2. In the **Add New Item** dialog:
    - In the left pane, click `Installed/Visual C#/ASP.NET Core/Code`.
    - In the middle pane, click **Class**.
    - At the bottom, in the **Name** field, enter `Templates.cs` and click **Add**.
3. In the class file, add the following content:

    ```csharp
    using Sitecore.Data;

    namespace MyProject
    {
      public static class Templates
      {
        public static class SiteRoot
        {
          public static readonly ID Id = ID.Parse("{061CBA15-5474-4B91-8A06-17903B102B82}");
        }

        public static class AppRoute
        {
          public static readonly ID Id = ID.Parse("{89829df8-93ee-56be-b57a-8f9efa6ba0a6}");

          public static class Fields
          {
            public static readonly ID PageTitle = ID.Parse("{09265946-b966-5034-bad7-94e0d935147a}");
          }
        }
      }
    }
    ```

4. Save the class file.

</details>

<details>
<summary><h2>Create the contents resolver class</h2></summary>

The contents resolver class overrides the `ResolveContents` method of the `Sitecore.LayoutService.ItemRendering.ContentsResolvers.RenderingContentsResolver` class and returns a content tree object.

> [!important]
> The `ResolveContents` method *must* return an object that can be returned to the rendering host as a JSON rendering.

To create the contents resolver class in the `Platform` project:

1. In Visual Studio, in Solution Explorer, right-click `Platform` and click **Add**, **New Item**.
2. In the **Add New Item** window:
    - In the left pane, click `Installed/Visual C#/ASP.NET Core/Code`.
    - In the middle pane, click **Class**.
    - At the bottom, in the **Name** field, enter `NavigationContentsResolver.cs` and click **Add**.
3. In the class file, enter the following content:

    ```csharp
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Sitecore.Abstractions;
    using Sitecore.Data.Items;
    using Sitecore.LayoutService.Configuration;
    using Sitecore.LayoutService.ItemRendering.ContentsResolvers;
    using Sitecore.Mvc.Presentation;

    namespace MyProject.LayoutService
    {
      public class NavigationContentsResolver : RenderingContentsResolver
      {
        private const int NavigationDepth = 2;

        private readonly BaseLinkManager _linkManager;

        public NavigationContentsResolver(BaseLinkManager linkManager)
        {
          _linkManager = linkManager;
        }

        public override object ResolveContents(Rendering rendering, IRenderingConfiguration renderingConfig)
        {
          var contextItem = GetContextItem(rendering, renderingConfig);
          var siteRoot = contextItem?.Axes.GetAncestors().FirstOrDefault(item => item.DescendsFrom(Templates.SiteRoot.Id));
          if (siteRoot == null)
          {
            return null;
          }

          // First page under our site root should be Home
          return GetNavigation(siteRoot, NavigationDepth).FirstOrDefault();
        }

        private IEnumerable<object> GetNavigation(Item parent, int depth)
        {
          depth--;
          return parent.Children
            .Where(item => item.DescendsFrom(Templates.AppRoute.Id))
            .Select(item => new
            {
              Title = item[Templates.AppRoute.Fields.PageTitle],
              Url = _linkManager.GetItemUrl(item),
              Children = depth >= 0 ? GetNavigation(item, depth) : new object[0]
            });
        }
      }
    }
    ```

4. Save the class file.
5. Right-click the `Platform` project and click **Publish**.
6. In the **Platform** window, click **Publish** to publish the `Platform` project in the Sitecore Content Management and Content Delivery instances.

> [!tip]
> -   Navigation contents resolvers must avoid using `Sitecore.Context.Item` or another page-specific context to output data on current/active navigation items (like highlighting active navigation items in the navigation menu). This is better handled client-side and ensures you can cache navigation data across pages.
>
> -   Use Sitecore Content Search instead of Sitecore Query or other resource-expensive methods of navigating the content tree where possible.

</details>

<details>
<summary><h2>Create the contents resolver item</h2></summary>

The contents resolver item links the contents resolver class to Sitecore.

To create the contents resolver item in the Sitecore instance:

1. In the Content Editor, right-click `/sitecore/System/Modules/Layout Service/Rendering Contents Resolvers` and click **Insert**, **Rendering Contents Resolver**.

    > [!tip]
    > To optimize serialization, you can organize content resolvers into folders.

2. In the **Message** dialog, in the **Name** field, enter `Navigation Resolver` and click **OK**.
3. On the **Content** tab for the `Navigation Resolver` item, in the **Data** section, in the **Type** field, enter the name of the contents resolver:

    ```text
    MyProject.LayoutService.NavigationContentsResolver, MyProject
    ```

4. Select **Use Context Item** to make the contents resolver navigate the content tree relative to the current item (the Sitecore context page/route) when building the content tree model. If you do not select this check box, the contents resolver uses its datasource item.
5. Save the contents resolver item.

</details>

<details>
<summary><h2>Create the JSON rendering item</h2></summary>

The JSON rendering item renders the output of the contents resolver class (the content tree object) as JSON formatted data for the rendering host.

To create the JSON rendering item in the Sitecore instance:

1. In the Content Editor, right-click `/sitecore/Layout/Renderings` and click **Insert**, **Json Rendering**.
2. In the **Message** dialog, in the **Name** field, enter `Navigation Rendering` and click **OK**.
3. On the **Content** tab for the `Navigation Rendering` item, in the **Layout Service** section, in the **Rendering Contents Resolver** dropdown list, click the **Navigation Resolver** item.
4. Save the JSON rendering item.

> [!tip]
> If possible, configure output caching for renderings that use content resolvers. This is especially important for renderings that need access to many items, such as navigation.

</details>

<details>
<summary><h2>Add the JSON rendering item to the standard values of the AppRoute template</h2></summary>

By adding the JSON rendering item to the `AppRoute` templates standard values, all pages that inherit from the `AppRoute` template include the JSON rendering.

To add the JSON rendering item to the standard values of `AppRoute` templates:

1. In Content Editor, click `/sitecore/Templates/Project/MyProject/AppRoute/__Standard Values`.
2. On the **Presentation** tab, click **Details**.
3. In the **Layout Details** dialog, in the **Default** section, click **Edit**.
4. In the **Device Editor** dialog, on the **Layout** tab, select `Layouts/Project/MyProject/Main`.
5. On the **Controls** tab, click **Add**.
6. In the **Select a Rendering** dialog, click `Renderings/Navigation Rendering`.
7. In the **Add to Placeholder** field, click `Main` and click **Select**.
8. In the **Device Editor** dialog, click **OK**.
9. In the **Layout Details** dialog, click **OK**.

</details>

<details>
<summary><h2>Test the contents resolver</h2></summary>

Before starting on the model-bound view, test that the Sitecore Layout Service includes the contents resolver JSON rendering in its output to the rendering host.

See [how to publish, run, and test the Getting Started template items and projects](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/publish,-run,-and-test-the-getting-started-template-projects.html "Publish, run, and test the Getting Started template projects").

Depending on your Rendering Engine configuration, the rendering host page might output an `Unknown component` or a similar message. This is perfectly normal.

In this sample JSON rendering, we have added some items to the content tree to populate the navigation model:

```csharp
{
  "uid":"19ea6f2e-3ab9-4e9d-b3a6-9a0e5a6e9d0a",
  "componentName":"Navigation Rendering",
  "dataSource":"",
  "params":{},
  "fields":{
    "title":"Welcome to Sitecore",
    "url":"/en/",
    "children":[
      {
        "title":"Main Nav 1",
        "url":"/en/MainNav1",
        "children":[
          {
            "title":"Child Nav 1",
            "url":"/en/MainNav1/ChildNav1",
            "children":[]
          },
          {
            "title":"Child Nav 2",
            "url":"/en/MainNav1/ChildNav2",
            "children":[]
          }
        ]
      },
      {
        "title":"Main Nav 2",
        "url":"/en/MainNav2",
        "children":[
          {
            "title":"More Child Nav",
            "url":"/en/MainNav2/MoreChildNav",
            "children":[]
          }
        ]
      },
      {
        "title":"Main Nav 3",
        "url":"/en/MainNav3",
        "children":[]
      }
    ]
  }
}

```

</details>

<details>
<summary><h2>Create the model</h2></summary>

The model provides a strongly typed class that maps to the data in the contents resolver JSON rendering returned by the Sitecore Layout Service.

To create the model in the `RenderingHost` project:

1. In Visual Studio, in Solution Explorer, right-click `RenderingHost/Models` and click **Add**, **New Item**.
2. In the **Add New Item** window:
    - In the left pane, click `Installed/Visual C#/ASP.NET Core/Code`.
    - In the middle pane, click **Class**.
    - At the bottom, in the **Name** field, enter `NavigationModel.cs`.
    - Click **Add**.
3. In the class file, to bind the JSON rendering to the model, add the following content:

    ```csharp
    using System.Collections.Generic;
    using Sitecore.AspNet.RenderingEngine.Binding.Attributes;

    namespace MyProject.Models
    {
      public class NavigationModel
      {
        [SitecoreComponentField]
        public string Title { get; set; }

        [SitecoreComponentField]
        public string Url { get; set; }

        [SitecoreComponentField]
        public IEnumerable<NavigationModel> Children { get; set; }
      }
    }
    ```

    > [!note]
    > Because you are not binding directly to Sitecore fields, you can use simple types like `string` in your model, but you must explicitly attribute them with `[SitecoreComponentField]`.

4. Save the class file.

</details>

<details>
<summary><h2>Create the model-bound view</h2></summary>

The [model-bound view](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/model-bound-views.html "Model-bound views") renders the model data as HTML.

To create the model-bound view in the `RenderingHost` project:

1. In Visual Studio, in Solution Explorer, right-click `RenderingHost/Views/Shared/Components/SitecoreComponent` and click **Add**, **New Item**.
2. In the **Add New Item** window:
    - In the left pane, click `Installed/Visual C#/ASP.NET Core/Web`.
    - In the middle pane, click **Razor View**.
    - At the bottom, in the **Name** field, enter `Navigation Rendering.cshtml`.

        > [!important]
        > You get an `Unknown Component` error if the name of the model-bound view file does not match the name of the JSON rendering.

    - Click **Add**.
3. In the model-bound view file, add the following content:

    ```csharp
    @model NavigationModel

    <ul>
      <li><a href="@Model.Url">Home</a></li>
      @foreach (var navigation in Model.Children)
      {
        <li>
        <a href="@navigation.Url">@navigation.Title</a>
        @if (@navigation.Children != null)
        {
          <ul>
          @foreach (var childNav in navigation.Children)
          {
            <li><a href="@childNav.Url">@childNav.Title</a></li>
          }
          </ul>
        }
        </li>
      }
    </ul>
    ```

4. Save the model-bound view file.

</details>

<details>
<summary><h2>Register the model-bound view</h2></summary>

You must map the model-bound view to the layout service component name (both are called `Navigation Rendering`) in the [Sitecore Rendering Engine](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/asp-net-core-rendering-engine.html "ASP.NET Core Rendering Engine").

To register the model-bound view in the `Startup` class:

1. In Visual Studio, in Solution Explorer, double-click the `Startup.cs` file.
2. In the `Startup.cs` file, in the `services.AddSitecoreRenderingEngine` call, register the model-bound view and the model as a Rendering Engine component with the `[AddModelBoundView()](https://doc.sitecore.com/resources/sitecore-asp-dot-net-rendering-sdk/api/Sitecore.AspNet.RenderingEngine.Extensions.RenderingEngineOptionsExtensions.html#Sitecore_AspNet_RenderingEngine_Extensions_RenderingEngineOptionsExtensions_AddModelBoundView__1_Sitecore_AspNet_RenderingEngine_Configuration_RenderingEngineOptions_System_String_)` method:

    ```csharp
    services.AddSitecoreRenderingEngine(options =>
      {
        //Register your components here
        options
          .AddModelBoundView<ContentBlockModel>("ContentBlock")
          .AddModelBoundView<NavigationModel>("Navigation Rendering")
          .AddDefaultPartialView("_ComponentNotFound");
      })
      // Includes forwarding of Scheme as X-Forwarded-Proto to the Layout Service so that
      // Sitecore Media and other links have the correct scheme.
      .ForwardHeaders()
      // Enable forwarding relevant headers and client IP for Sitecore Tracking and Personalization.
      .WithTracking()
      // Enable support for the Experience Editor.
      .WithExperienceEditor(options =>
      {
        // Experience Editor integration needs to know the external URL of your rendering host,
        // if behind HTTPS termination or another proxy (like Traefik).
        if (Configuration.RenderingHostUri != null)
        {
          options.ApplicationUrl = Configuration.RenderingHostUri;
        }
      });
    ```

    > [!important]
    > You get an `Unknown Component` error if the name of the model-bound view file does not match the name of the JSON rendering.

3. Save the `Startup.cs` file.

</details>

<details>
<summary><h2>Test the model-bound view</h2></summary>

See [how to publish, run, and test the Getting Started template items and projects](https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/publish,-run,-and-test-the-getting-started-template-projects.html "Publish, run, and test the Getting Started template projects").

> [!tip]
> Review the `README` files in the projects and the solution for additional information.

</details>