Optional Razor Sections with Default Content

Solution quick links:

  1. IsSectionDefined method
  2. Razor inline templates

RenderSection

The new ASP.NET page framework built on Razor (which is available in MVC 3) provides a facility for content pages to contribute named fragments of markup to their layout pages which the layout page can then render in an arbitrary location using the RenderSection method.

For example, the following content page declares an "ExtraContent" section:

 @{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@section ExtraContent {
    <div>Some extra content</div>
}

<div>The main content</div>

And the following layout page renders it:

 <!DOCTYPE html>
<html>
<head></head>
<body>
@RenderBody()

@RenderSection("ExtraContent")
@RenderSection("OptionalContent", required: false)
</body>
</html>

You can even declare that a section is not required like the “OptionalContent” section in the example above.

But what if you want to have some default content for your optional sections?

Option 1: Use the IsSectionDefined method

The IsSectionDefined method returns true if a child content page defined a section. You can use that to decide whether to render a section or some other content:

 <!DOCTYPE html>
<html>
<body>
@RenderBody()

@if (IsSectionDefined("OptionalContent")) { 
    @RenderSection("OptionalContent")
}
else { 
    <div>Default content</div>
}
</body>
</html>

Just remember that you need to use the @RenderSection() syntax (you need the @ character) so that the contents of the section is actually printed to the output. Without that character you will get an exception with the following message:

The following sections have been defined but have not been rendered for the layout page "~/Views/Shared/_Layout.cshtml": "OptionalContent"

Option 2: Use Razor inline templates

Razor provides a way to pass inline markup templates into function calls. This is a powerful mechanism that allows you to combine custom markup fragments with general purpose display logic. In order to make the following example work you will need to drop the following code into your project:

 using System.Web.WebPages;
using System;

public static class SectionExtensions {
    private static readonly object _o = new object();
    public static HelperResult RenderSection(this WebPageBase page,
                        string sectionName,
                        Func<object, HelperResult> defaultContent) {
        if (page.IsSectionDefined(sectionName)) {
            return page.RenderSection(sectionName);
        }
        else {
            return defaultContent(_o);
        }
    }
}

This code essentially wraps the previous example in a reusable extension method. All you need now is to provide the default content markup. With it you can write views like the following:

 <!DOCTYPE html>
<html>
<body>
@RenderBody()

@this.RenderSection("OptionalContent",
                    @<div>Default content</div>)
</body>
</html>

For the second parameter you need to use the @ character to trigger the Razor parser to go into markup mode. Once in markup mode you can do anything that you do in the main body of the page.

Just remember that since this is an extension method you need to call it off the this object. Otherwise the C# compiler will not be able to locate it and you will get a compilation error.

If you like this technique read the follow up: Razor, Nested Layouts and Redefined Sections