Kentico Xperience Design Patterns: Good Layout Hygiene
Tags: xperience, kentico, aspnetcore, csharp
Published: 2021-07-12 15:57:50

---
title: Kentico Xperience Design Patterns: Good Layout Hygiene
published: true
description: Learn how to maintain a clean and focused _Layout.cshtml file using Razor Partials and View Components.
tags: xperience, kentico, aspnetcore, csharp
series: Kentico Xperience Design Patterns
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/hcusgcjdsp4aozej50g9.jpg
---

It can be easy for Kentico Xperience developers to focus applying well known software design patterns, like DRY, composition, abstraction, and separation of concerns to their C# code, but these patterns are just as important in Razor code 🧐.

We can use Page Builder Widget Sections and Widgets to decompose the Page layout and design into reusable pieces, but what about the parts of a site common to every Page, like navigation, headers, footers, meta elements, and references to CSS and JavaScript 🤷🏽‍♂️?

By applying the above design patterns we can make sure we practice good Layout hygiene 🚿, keeping our Razor `_Layout.cshtml` organized and maintainable.

> If you are looking for other tips on keeping a clean Kentico Xperience application, checkout my post [Kentico Xperience Design Patterns: Good Startup.cs Hygiene ](https://dev.to/seangwright/kentico-xperience-design-patterns-good-startup-cs-hygiene-3klm).

## 📚 What Will We Learn?

- [What is a Razor Layout](#what-is-a-layout)
- [The problems with unmaintained Razor Layouts](#an-unmaintained-layout)
- [When to use Partial Views and View Components](#janitorial-tools)
- [How to clean up and organize a messy Layout](#cleaning-up-a-messy-layout)

## 🚀 [What is a Layout?](#what-is-a-layout)

According to the [documentation for ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-5.0), a Layout "defines a top level template for views in the app."

By convention, the `_Layout.cshtml` file in our `~/Views/Shared` folder is the Layout used by all Views in our application. This can be changed by modifying the `Layout` value specified in `_ViewStart.cshtml` at the root of the project or by overriding the `Layout` value on a per-View basis 🤓.

The Layout is where we define all the markup that should appear on all (or most) Pages in our site. This includes headers and footers (navigation), `<script>` and `<link>` elements, marketing tags, meta tags, and any markup that wraps the main body of our Razor Views.

If we take a [semantic HTML](https://dev.to/kenbellows/stop-using-so-many-divs-an-intro-to-semantic-html-3i9i) approach to our markup, we might have something that looks like this:

```html
<head>
    <!-- links, meta tags -->
</head>
<body>
    <header>
        <!-- nav, banners -->
    <header>

    <main>
        <!-- page content -->
        @RenderBody()
    </main>

    <footer>
        <!-- nav, social icons -->
    </footer>
</body>
```

`@RenderBody()` is where the markup from our Page specific Views will end up, and everything else is rendered _from_ our Layout (but not necessarily _by_ our Layout 😉).

Some of this markup might be the same between Pages (navigation) and other parts will be more dynamic, including Page specific content, like [Open Graph meta tags](https://ogp.me/).

## 🚽 [An Unmaintained Layout](#an-unmaintained-layout)

If we look at the [Dancing Goat sample site](https://docs.xperience.io/installation/quick-installation#Quickinstallation-StartingtheDancingGoatsamplesite)'s `_Layout.cshtml` we can see what happens when our Layout grows in complexity to serve the needs of our site's functionality.

It's about 160 lines long and renders the following:

- Static meta
- Kentico Xperience dynamic (page specific) meta
- Kentico Xperience marketing features scripts
- Kentico Xperience Page Builder script and styles
- The site's styles
- A tracking consent form
- The site's header
  - Navigation
  - Authenticated user avatar
  - Shopping cart icon
  - A language/culture switcher
  - Search box
- The site's main content container
- The site's footer
  - Company address
  - Social links
  - Newsletter subscription form
- The site's JavaScript

> Here's [a link](https://gist.github.com/seangwright/7265642e82736dcade11e57e9a0d84da) to the `_Layout.cshtml` in case you don't have access to it.

This seems like way too much for 1 file, but it is entirely plausible that an unmaintained Layout could evolve into this.

The main problem is that it's hard to reason about 😵! There's simply too much going on in 1 file. We can see some Razor code blocks that define C# variables - these are effectively creating global variables for the Razor file and global variables always make code more confusing 👍🏿.

If we remember that Razor files [generate C# classes](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0#directives) at compile time, it's easier to realize the benefits we might gain from simplifying this file - a C# class with this kind of complexity is definitely a [code smell](https://www.martinfowler.com/bliki/CodeSmell.html).

This Layout is also more likely to result in merge conflicts because of its size and mixed purposes. If a developer is working on an entirely different feature than another teammate, a well structured application will make it unlikely for them both to have to modify the same file for their changes. Merge conflicts, in this scenario, can also sometimes be a code smell 👃🏼 - especially if they are painful to resolve.

Maybe we could clean 🧼 it up? 

> The Dancing Goat site [is meant to demo Kentico Xperience's capabilities](https://devnet.kentico.com/articles/kentico-xperience-sample-sites-and-their-differences), not be the pinnacle of software architecture 😋.

## 🧹 [Janitorial Tools](#janitorial-tools)

So what tools ⚒ can we use to clean up this smelly 💩 mess?

### 🧩 Partial Views

[Partial Views](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-5.0) are perfect for encapsulating a section of markup and giving it a name. They make it easier to understand and modify the markup in the Partial and do the same wherever the Partial is referenced - in our case, that's going to be the Layout.

If we are trying to reduce merge conflicts and make a bit of markup more readable, Partial Views are a great solution.

Partials can be passed parameters that become the View Model of the Partial, however these are not strongly typed and if the wrong type is passed as the View Model, we'll experience a runtime exception.

In ASP.NET Core, if we need to pass some data to a Partial, we might instead reach for another powerful tool - the View Component.

### 🖼 View Components

[View Components](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-5.0) give us an opportunity to separate data and logic from the declarative presentation of HTML. They mirror the MVC pattern by having 3 parts - the View Component class (Controller), View Model class, and Razor View.

Using the Tag Helper syntax for View Components (ex: `<vc:our-view-component>`), we get strongly typed parameters passed to the View Component, which can improve the developer experience and make our code more refactor-proof 😎.

Since View Component classes participate in dependency injection and have access to all the same helper 'context' properties that Controllers do, we can use them in much the same way. If we need access to state, services, or execute some logic to restructure a model, View Components are perfect 👏🏻.

We might see examples of injecting services directly into Views using [View Service Injection](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/dependency-injection?view=aspnetcore-5.0). While this technique is _very_ convenient, I advise against using it too much 😮. Views are declarative presentation concerns and any complex logic or data access should definitely be performed in C# classes.

As a concrete example, using an injected `IHtmlLocalizer` for localizing content is a great use-case for View Service Injection 💪🏾, however accessing a repository or any of Kentico Xperience's 'Retriever' services (`IPageRetriever`, `IPageAttachmentUrlRetriever`) in a View should be considered an anti-pattern - use a Controller or View Component instead.

> If you want to learn more about using View Components in Kentico Xperience, checkout my post [Kentico Xperience 13 Beta 3 - Page Builder View Components in ASP.NET Core ](https://dev.to/seangwright/kentico-xperience-13-beta-3-page-builder-view-components-in-asp-net-core-onm) or [Kentico Xperience Design Patterns: MVC is Dead, Long Live PTVC ](https://dev.to/seangwright/kentico-xperience-design-patterns-mvc-is-dead-long-live-ptvc-4635).

Now that we've covered our motivations for cleaning up our Layout and the tools we can use, let's jump into the task at hand.

## 🚿 [Cleaning Up a Messy Layout](#cleaning-up-a-messy-layout)

There's two observations we are going to make about the code in the Layout:

- What bits of markup share a common purpose?
- What needs application state or context to render?

Let's analyze these below...

### 🎨 Abstract Based on Purpose

First, we should identify which parts of the Layout belong together 👩🏾‍🤝‍🧑🏼 based on their purpose. Things that change together should stay together and we can relate this recommendation to the [Single-Responsibility Principle](https://en.wikipedia.org/wiki/SOLID).

> The lack of a single responsibility for this giant Layout is what makes it so complex.

We already have an outline of the different parts of our Layout, organized by purpose, with our original list at the beginning of this post, but let's look from an even higher 🌍 level.

#### HTML Head Element

The `<head>` element is an obvious starting point for refactoring:

```html
<head id="head">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta charset="UTF-8" />
    @Html.Kentico().PageDescription()
    @Html.Kentico().PageKeywords()
    @Html.Kentico().PageTitle(ViewBag.Title as string)
    <link rel="icon" href="~/content/images/favicon.svg" type="image/svg+xml" />
    <link href="~/Content/Styles/Site.css" rel="stylesheet" type="text/css" />
    <link rel="canonical" href="@Url.Kentico().PageCanonicalUrl()" />
    @RenderSection("styles", required: false)
    @Html.Kentico().ABTestLoggerScript()
    @Html.Kentico().ActivityLoggingScript()
    @Html.Kentico().WebAnalyticsLoggingScript()
    <page-builder-styles />
</head>
```

We can take this whole block of code and move it into a Partial view. I typically like to leave the top level elements (like `<head>` and `<body>`) in the Layout, so let's just take the contents of the `<head>` and move them to a new Partial `~/Views/Shared/_Head.cshtml`.

We'll also need to move over the 2 namespaces being used by the HtmlHelpers in this Partial:

```html
@using Kentico.OnlineMarketing.Web.Mvc
@using Kentico.Activities.Web.Mvc
```

This will change the Layout to the following:

```html
<!DOCTYPE html>

<html>
<head id="head">
    <partial name="_Head" />

    @RenderSection("styles", required: false)
</head>
```

We have to move the `@RenderSection()` out of the Razor we copied to the `_Head.cshtml` Partial because Mvc doesn't support rendering sections from Partials.

#### HTML Header Element

Next, we'll move the `<header>` element and all of its contents to a new `~/Views/Shared/_Header` Partial (along with the `@using Kentico.Membership.Web.Mvc` `using` that gives us access to the `AvatarUrl()` `HtmlHelper` extension).

This will reduce our Layout to the following, with a total of 59 lines for the file 🥳:

```html
<!DOCTYPE html>

<html>
<head id="head">
    <partial name="_Head" />
</head>
<body class="@ViewData["PageClass"]">
    <div class="page-wrap">
        <vc:tracking-consent />

        <partial name="_Header" />

<!-- ... -->
```

The `_Header` Partial can be simplified further but let's finish working with the Layout first.

#### Footer HTML Element

We only have a few sections of markup left in our Layout and the next one that we'll abstract into a Partial is the 'footer'.

The Dancing Goat footer includes a `<div class="footer-wrapper">` container element. Even though the actual `<footer>` element is nested inside it, the wrapper is part of the footer from a design perspective, so let's take the wrapper and all of its contents and move them to a new Partial `~/Views/Shared/_Footer.cshtml`. We'll also need to move the `@using DancingGoat.Widgets` `using` to our Partial to get access to the Newsletter subscription widget types.

Our updated Layout is shaping up 😊 and looks as follows:

```html
<body class="@ViewData["PageClass"]">
    <div class="page-wrap">
        <vc:tracking-consent />

        <partial name="_Header" />
        
        <div class="container">
            <div class="page-container-inner">
                @RenderBody()

                <div class="clear"></div>
            </div>
        </div>
    </div>

    <partial name="_Footer" />

    <!-- ... -->
```

#### Page Scripts

The last bit of markup to abstract out of our Layout includes all the `<script>` tags, which we will move to another new Partial `~/Views/Shared/_Scripts.cshtml`. We will leave the `@RenderSection("scripts", required: false)` call in the Layout, just like the `RenderSection` call for styles.

We've completely cleaned up the Layout and trimmed it down to a lovely 27 lines 😅:

```html
<!DOCTYPE html>
<html>
<head id="head">
    <partial name="_Head" />
</head>
<body class="@ViewData["PageClass"]">
    <div class="page-wrap">
        <vc:tracking-consent />

        <partial name="_Header" />
        
        <div class="container">
            <div class="page-container-inner">
                @RenderBody()

                <div class="clear"></div>
            </div>
        </div>
    </div>

    <partial name="_Footer" />

    <partial name="_Scripts" />

    @RenderSection("scripts", required: false)
</body>
</html>
```

We were able to do this without creating any new abstractions - just organizing code by common concerns. In our Layout, these common concerns can often be identified by high level HTML elements, like `<head>`, `<header>`, and `<footer>`.

> This clean up also lets us see additional places we could insert `@RenderSection()` calls if we wanted to give our Views more places to hook into the Layout 🤔.

### 🔬 Identify What Needs State

The `_Footer` and `_Head` Partials are simple and only a few lines of code. However, the `_Header` Partial we created is 88 lines long. It includes a Razor code block, has multiple conditional statements, and 2 navigation blocks 😨 (header navigation and the 'additional' navigation).

The Razor code block in the view is often a code smell - it indicates we are executing some logic in our View. This specific code block not only executes some logic, but it also access application state - the `@ViewContext`.

The `_Header` Partial also accesses state through the `ClaimsPrincipal User` and the `HttpContext Context` properties of the Razor page. While these properties are accessible from the Razor view, I like to think of them as a convenience and not necessarily something that should be used when considering best practices for larger applications.

Any time we have logic in our Layout (or Partials), and especially when we are accessing application state, I see it as an opportunity to leverage a View Component instead of a Partial 🤓.

View Components have access to the same context specific state (like `ViewContext`, `User`, and  `Context`) but they are more testable and appropriate for C# code and logic 👍🏼.

#### Header View Component

The only portion of the `_Header` Partial using this context state is the contents of the `<ul class="additional-menu">` element, so let's use that as the starting point for our View Component.

Let's create a new View Component `~/Components/ViewComponents/HeaderMenu/HeaderMenuViewComponent.cs`:

```csharp
public class HeaderMenuViewComponent : ViewComponent
{
    public IViewComponentResult Invoke()
    {
        string cultureCode = ViewContext
            .RouteData
            .Values["culture"];

        var currentCultureCode = Convert.ToString(cultureCode);

        var currentLanguage = currentCultureCode.Equals(
            "es-es", StringComparison.OrdinalIgnoreCase) 
            ? "ES" : "EN";

        bool isCultureSelectorVisible = HttpContext.Response.StatusCode == (int)System.Net.HttpStatusCode.OK;

        var vm = new HeaderMenuViewModel(
            User.Identity.IsAuthenticated,
            currentLanguage,
            isCultureSelectorVisible);

        return View(
            "~/Components/ViewComponents/HeaderMenu/_HeaderMenu.cshtml", vm);
    }
}

public record HeaderMenuViewModel(
    bool IsUserAuthenticated,
    string CurrentLanguage,
    bool IsCultureSelectorVisible);
```

We've moved all of the logic of our View into the View Component class and created a View Model `record` that represents the state we need to expose to the View, with property names that have a clear purpose.

We can now move the `<ul class="additional-menu">` element and its contents into `~/Components/ViewComponents/HeaderMenu/_HeaderMenu.cshtml` add a `@model DancingGoat.Components.ViewComponents.HeaderMenu.HeaderMenuViewModel` directive at the top of the file, and use the View Model properties instead of the context helper properties.

We can see the effect this has on the culture selector in the View Component View 🤩:

```html
@if (Model.IsCultureSelectorVisible)
{
    <li class="dropdown">
        <a class="dropbtn">@Model.CurrentLanguage</a>
        <div class="dropdown-content">
            <culture-link link-text="English" culture-name="en-US" />
            <culture-link link-text="Español" culture-name="es-ES" />
        </div>
    </li>
}
```

We can now update the `~/Views/Shared/_Header.cshtml` Partial, replacing the former location of `<ul class="additional-menu">` with a reference to our View Component `<vc:header-menu />`:

```html
<header data-ktc-search-exclude>
    <nav class="navigation">
        <div class="nav-logo">
            <div class="logo-wrapper">
                <a href="@Url.Kentico().PageUrl(ContentItemIdentifiers.HOME)" class="logo-link">
                    <img class="logo-image" alt="Dancing Goat" src="~/Content/Images/logo.svg" />
                </a>
            </div>
        </div>
        <vc:navigation footer-navigation="false" />

        <vc:header-menu /> <!-- 😃 -->
    </nav>
    <div class="search-mobile">
        <form asp-action="Index" asp-controller="Search" method="get" class="searchBox">
            <input name="searchtext" type="text" placeholder="@HtmlLocalizer["Search"]" autocomplete="off" />
            <input type="submit" value="" class="search-box-btn" />
        </form>
    </div>
</header>
```

#### If Tag Helper

Coming from ASP.NET MVC 5 in Kentico 12, we are probably used to seeing Html Helper calls and Razor statements all over our Views.

I've found that adopting [Tag Helpers](https://docs.microsoft.com/en-US/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-5.0), which look much more like HTML, leads to more readable Views. As a bonus, if you've had experience with any client-side JavaScript frameworks, like React, Vue, or Angular, ASP.NET Core Tag Helpers are going to look like the server-side equivalents of what those frameworks already support.

To this end, it could be nice 😏 to replace the Razor `@if()` syntax with a Tag Helper. First, let's see what we're working with:

```html
<div class="dropdown-content">
    @if (Model.IsUserAuthenticated)
    {
        <a asp-controller="Account" asp-action="YourAccount">
            @HtmlLocalizer["Your&nbsp;account"]
        </a>
        <form method="post" asp-controller="Account" 
                            asp-action="Logout">
            <input type="submit" 
                   value="@HtmlLocalizer["Sign out"]" 
                   class="sign-out-button" />
        </form>
    }
    else
    {
        <a asp-controller="Account" asp-action="Register">
            @HtmlLocalizer["Register"]
        </a>
        <a asp-controller="Account" asp-action="Login">
            @HtmlLocalizer["Login"]
        </a>
    }
</div>
```

Using the `<if>` Tag Helper detailed by Andrew Lock in [this blog post](https://andrewlock.net/creating-an-if-tag-helper-to-conditionally-render-content/), we can update our View to look more HTML-ish:

```html
<div class="dropdown-content">
    <if include-if="Model.IsUserAuthenticated">
        <a asp-controller="Account" asp-action="YourAccount">
            @HtmlLocalizer["Your&nbsp;account"]
        </a>
        <form method="post" asp-controller="Account" 
                            asp-action="Logout">
            <input type="submit" 
                   value="@HtmlLocalizer["Sign out"]" 
                   class="sign-out-button" />
        </form>
    </if>
    <if exclude-if="Model.IsUserAuthenticated">
        <a asp-controller="Account" asp-action="Register">
            @HtmlLocalizer["Register"]
        </a>
        <a asp-controller="Account" asp-action="Login">
            @HtmlLocalizer["Login"]
        </a>
    </if>
</div>
```

This is a matter of taste 🥪, but I've found many of the ASP.NET Core Tag Helpers to be much more readable than their Html Helper and Razor syntax equivalents (including the [Partial Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/partial-tag-helper?view=aspnetcore-5.0) and [View Component Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-5.0#invoking-a-view-component-as-a-tag-helper) we've already used).

## 🧠 Conclusion

We've reduced the size of the Dancing Goat `_Layout.cshtml` file from 160 lines to 27 and the largest Partial or View Component View that we now have from our refactoring is the `_HeaderMenu.cshtml` at 64 lines.

We could continue the refactoring with additional View Components or Partial Views to reduce that size even further - likely separating the Avatar markup and Login/Register links next. I'll leave that task for the now-very-capable reader 😁.

By identifying a problematic area of the application, the tools that ASP.NET Core puts at our disposal, and applying some common refactoring patterns, we've created a clean an maintainable Layout.

Let me know ✍ your patterns and practices for Layouts in Kentico Xperience applications in the comments below...

As always, thanks for reading 🙏!

---

<figcaption>Photo by <a href="https://unsplash.com/@markusspiske">Markus Spiske
</a> on <a href="https://unsplash.com">Unsplash</a></figcaption>

---

## References

- [Kentico Xperience Design Patterns: Good Startup.cs Hygiene ](https://dev.to/seangwright/kentico-xperience-design-patterns-good-startup-cs-hygiene-3klm)
- [ASP.NET Core Docs - Layouts](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-5.0)
- [Stop using so many divs! An intro to semantic HTML ](https://dev.to/kenbellows/stop-using-so-many-divs-an-intro-to-semantic-html-3i9i)
- [Kentico Xperience Docs - Dancing Goat Sample Site Installation Process](https://docs.xperience.io/installation/quick-installation#Quickinstallation-StartingtheDancingGoatsamplesite)
- [Kentico Xperience sample sites and their differences](https://devnet.kentico.com/articles/kentico-xperience-sample-sites-and-their-differences)
- [ASP.NET Core Docs - Razor Directives](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-5.0#directives)
- [Martin Fowler - Code Smells](https://www.martinfowler.com/bliki/CodeSmell.html)
- [ASP.NET Core Docs - Partial Views](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-5.0)
- [ASP.NET Core Docs - View Components](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-5.0)
- [ASP.NET Core Docs - View Service Injection](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/dependency-injection?view=aspnetcore-5.0)
- [Kentico Xperience 13 Beta 3 - Page Builder View Components in ASP.NET Core ](https://dev.to/seangwright/kentico-xperience-13-beta-3-page-builder-view-components-in-asp-net-core-onm)
- [Kentico Xperience Design Patterns: MVC is Dead, Long Live PTVC ](https://dev.to/seangwright/kentico-xperience-design-patterns-mvc-is-dead-long-live-ptvc-4635)
- [Single-Responsibility Principle](https://en.wikipedia.org/wiki/SOLID)
- [ASP.NET Core Docs - Tag Helpers](https://docs.microsoft.com/en-US/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-5.0)
- [Andrew Lock - If Tag Helper](https://andrewlock.net/creating-an-if-tag-helper-to-conditionally-render-content/)

---

We've put together a list over on [Kentico's GitHub account](https://github.com/Kentico/Home/blob/master/RESOURCES.md) of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico or Xperience tags here on DEV.

{% tag kentico %}

{% tag xperience %}

Or my [Kentico Xperience blog series](https://dev.to/seangwright/series), like:

- [Kentico Xperience Xplorations](https://dev.to/seangwright/series/8185)
- [Kentico Xperience MVC Widget Experiments](https://dev.to/seangwright/series/9483)
- [Bits of Xperience](https://dev.to/seangwright/series/8740)