Custom Content Rendering
Why customize content rendering
Section titled “Why customize content rendering”Every content type needs a visual representation. By default, CMS routes page requests to a controller and view based on the content type name. When you need custom logic — loading related content, transforming data, or selecting different layouts — you build custom controllers and views.
What you will do
Section titled “What you will do”- Create a page controller for a content type
- Build a Razor view that renders properties
- Add partial rendering for block types
- Register a display channel for mobile rendering
Create a page controller
Section titled “Create a page controller”A page controller handles HTTP requests for a specific page type. Inherit from PageController<T> where T is your page type.
using Microsoft.AspNetCore.Mvc;
using Optimizely.Cms.Core.Web;
namespace MySite.Controllers;
public class ArticlePageController : PageController<ArticlePage>
{
public IActionResult Index(ArticlePage currentPage)
{
var model = new ArticleViewModel
{
Title = currentPage.Title,
Body = currentPage.MainBody,
Author = currentPage.AuthorName,
Published = currentPage.StartPublish,
ReadTime = EstimateReadTime(currentPage.MainBody)
};
return View(model);
}
private static int EstimateReadTime(XhtmlString? body)
{
if (body == null) return 0;
var wordCount = body.ToString()?.Split(' ').Length ?? 0;
return Math.Max(1, wordCount / 200);
}
} The CMS routing engine automatically maps requests for ArticlePage content to this controller. The currentPage parameter is populated by the framework.
Build the Razor view
Section titled “Build the Razor view”Place the view at Views/ArticlePage/Index.cshtml to match the controller name convention.
@model MySite.Models.ArticleViewModel
<article class="article">
<header>
<h1>@Model.Title</h1>
<div class="meta">
<span>By @Model.Author</span>
<span>@Model.Published?.ToString("MMMM d, yyyy")</span>
<span>@Model.ReadTime min read</span>
</div>
</header>
<div class="article-body">
@Html.Raw(Model.Body)
</div>
</article> Render blocks with partial views
Section titled “Render blocks with partial views”Blocks render inside ContentArea properties. Create a partial controller that returns a partial view.
using Microsoft.AspNetCore.Mvc;
using Optimizely.Cms.Core.Web;
namespace MySite.Controllers;
public class TestimonialBlockController
: BlockController<TestimonialBlock>
{
public override IActionResult Index(
TestimonialBlock currentBlock)
{
return PartialView(currentBlock);
}
} Place the partial view at Views/TestimonialBlock/Index.cshtml.
@model MySite.Models.TestimonialBlock
<blockquote class="testimonial">
<p>"@Model.Quote"</p>
<footer>
<strong>@Model.AuthorName</strong>
<span>@Model.AuthorTitle</span>
</footer>
</blockquote> Register a display channel
Section titled “Register a display channel”Display channels let you serve different views for different device types — for example, a simplified layout for mobile.
using Optimizely.Cms.Core.Web;
using Optimizely.Cms.Framework.Web;
namespace MySite.Channels;
public class MobileChannel : DisplayChannel
{
public override string ChannelName => "Mobile";
public override string ResolutionId =>
typeof(MobileChannel).FullName!;
public override bool IsActive(
HttpContext context)
{
return context.Request.Headers["User-Agent"]
.ToString()
.Contains("Mobile", StringComparison.OrdinalIgnoreCase);
}
} When a mobile user agent is detected, the CMS looks for a view at Views/ArticlePage/Index.Mobile.cshtml. If it exists, that view is rendered instead of the default.
Use template descriptors for selection
Section titled “Use template descriptors for selection”When a content type has multiple possible renderings, register template descriptors so editors can choose.
using Optimizely.Cms.Core.Web;
namespace MySite.Controllers;
[TemplateDescriptor(
Name = "Wide Layout",
Description = "Full-width article layout",
AvailableWithoutTag = false,
Tags = new[] { "wide" })]
public class ArticleWideController
: PageController<ArticlePage>
{
public IActionResult Index(ArticlePage currentPage)
{
return View("IndexWide", currentPage);
}
} Editors select the rendering template from the page settings panel.
Common issues
Section titled “Common issues”| Issue | Cause | Fix |
|---|---|---|
| 404 on page request | No controller found for content type | Ensure controller class name matches {TypeName}Controller |
| View not found | View in wrong directory | Place views in Views/{TypeName}/Index.cshtml |
| Block renders as empty | Missing partial controller | Create a BlockController<T> for the block type |