Page Templates are awesome in Kentico Xperience 13 and they've gotten even better in Xperience by Kentico.
But, that doesn't mean there aren't some sharp corners when using them. Sometimes you can get everything setup perfectly but they still don't work! Other times you see an error message but have no idea what it means 🤷♂️. Let's look at some common misconfigurations and errors to better quickly diagnose issues when using Page Templates.
This post applies to Xperience by Kentico v27 and newer.
If you're using Kentico Xperience 13, check out this great post with some common ways to troubleshoot Page Builder problems.
# Page Template not registered exception
When using Page Templates, we are basically gluing some C# code to database records by telling Xperience "when you see a request for this page, use a Page Template with this identifier to render it".
The Page Template identifier is the key part of the glue. There's a SQL query you can run against your database to better understand how this gluing works.
SELECT WP.WebPageItemTreePath, CD.ContentItemCommonDataPageTemplateConfiguration
FROM CMS_ContentItemCommonData AS CD
INNER JOIN CMS_WebPageItem AS WP
ON CD.ContentItemCommonDataContentItemID = WP.WebPageItemContentItemID
This query will show all the Page Template configuration JSON for each web page in all website channels in a solution.
The results of this query will have some rows with a null
value for ContentItemCommonDataPageTemplateConfiguration
but others will have some JSON that looks something like this.
{
"identifier": "MyApp.HomePageTemplate",
...
}
When Xperience sees a web page has a value in this column, it looks for a registered Page Template with a matching identifier.
The Page Template is registered by its identifier with the [assembly: RegisterPageTemplate()]
attribute.
[assembly: RegisterPageTemplate(
identifier: "KenticoCommunity.HomePage_Default",
name: "Home Page - Default",
propertiesType: typeof(HomePageTemplateProperties),
customViewName: "/Features/Home/HomePage_Default.cshtml",
ContentTypeNames = new[] { HomePage.CONTENT_TYPE_NAME },
Description = "Default home page template",
IconClass = "icon-home"
)]
But, if no Page Template is registered, we'll see an error like the following.
An unhandled exception occurred while processing the request.
InvalidOperationException: Page template with identifier "MyApp.HomePageTemplate" is not registered in the application.
Kentico.PageBuilder.Web.Mvc.PageTemplates.TemplateResult.ExecuteResultInternal(ActionContext context)
Somewhere, the glue came unglued!
Let's look at some causes and how to resolve them.
Cause: We deleted the Page Template registration attribute but some web pages still reference its identifier in the database.
Resolution: Don't delete Page Templates until all web pages have been switched to use new ones. We can use the query above to identify which pages still use a specific template.
Cause: We renamed (or typo'd) the name of the Page Template identifier.
Resolution: This is the same as deleting the Page Template from Xperience's perspective. It's tryin to find it by the identifier but nothing matches. Either create a new registration (using the same View and Properties) and then update all pages to not use the incorrect template before deleting its registration, or fix the name in the template identifier.
Cause: Our template is registered in an external class library.
Resolution: We probably accidentally removed the AssemblyDiscoverable attribute in our class library. This attribute lets Xperience know it should scan the assembly for registrations of various things. This is opt-in to improve startup performance, but sometimes it's easy to forget.
I prefer to add it to my .csproj
file instead of a separate .cs
file with 1 attribute in it.
<ItemGroup>
<AssemblyAttribute Include="CMS.AssemblyDiscoverableAttribute">
</AssemblyAttribute>
</ItemGroup>
# Page Template missing from the available templates list
Sometimes we've defined our Page Templates but don't see a specific one when we go to create a new web page.
This can happen from the same causes as the above scenario, but it could also be caused by how we define our Page Templates.
Page Templates can be made available to a website channel through Page Template filters. These filters decide which templates to show in the Admin UI when creating a new web page, based on the context of the request.
Page Template filters only execute when selecting a Page Template in the Admin UI. They are not used when rendering a web page that uses a Page Template. Changing filters will not affect any web pages already created for people visiting the site - only for people building the web site in the Page Builder.
If the Page Template filter is defined in a way that excludes a Page Template for a given web page, it won't be visible in the Page Builder UI.
The cause could also be much simpler! Page Templates can be registered with an optional ContentTypeNames
property. This property is like an auto-implemented filter - it will only allow the Page Template to be used for web pages matching the Content Types specified for this property.
If a web page Content Type name isn't in this array, the Page Template will not be available for web pages of this type.
# The View on path ... could not be found
This issue is extremely common, in my experience. Page Templates are registered, all the configuration is and the Page Template is configured to work with a specific web page Content Type. But, when we go to view the web page we see this exception.
InvalidOperationException: The view on path '/Views/Shared/PageTypes/<ClassName>.cshtml'
could not be found and there is no page template registered for selected page
The problem here is, once a web page is created Xperience treats it either as a Page Template page or a non-Page Template page and you can't switch between them.
If we create a web page before we register our Page Template, that web page is seen by Xperience as a normal MVC rendered page. It has no value in its ContentItemCommonDataPageTemplateConfiguration
database column.
Xperience's logic for finding the right code to execute to render a page can be complex, but for our scenario here, we can simplify it to these steps.
- Find the web page that the current request matches.
- Does that page have a value in the
ContentItemCommonDataPageTemplateConfiguration
database column? - If yes, find the registered Page Template based on the
identifier
value. - If no, find the MVC Controller and Action registered for this web page Content Type.
- If no MVC Controller and Action are registered, use routing conventions to look for a Razor View at
~/Views/Shared/PageTypes/<ClassName>.cshtml
. - If no View is found there, throw the above exception.
So, we can see here that when we except an existing, non-Page Template page to use a Page Template, it will likely fall through the above steps and result in an exception being thrown.
The solution is to always create and register Page Templates before creating web pages that we want to use them.
This is why I recommend using Page Templates for all pages.
If we don't need the ability to switch between templates just yet, we can just create 1 template and marketers don't ever have to know because that template will be auto-selected during web page creation.
But, when the time comes to create a new template, our web page is already a "page template page" and as soon as the new template is registered, the option to switch between the available templates will be visible in the Page Builder UI.
# Page Template view path incorrect
If we see an error like this, it probably means we have a typo in the customViewPath
parameter of the template registration attribute.
An unhandled exception occurred while processing the request.
InvalidOperationException: The view '/PageTemplates/LandingPage/MyTemplate.cshtml' was not found. The following locations were searched:
~/PageTemplates/LandingPage/MyTemplate.cshtml
Make sure the path on the file system matches the path in the attribute.
If we see a longer error, like the following, then it means we aren't using the customViewPath
and Xperience is looking for the Page Template View based on its conventions.
The convention based path for Page Templates involves taking the template identifier, replacing .
with _
, prefixing the identifier with an _
and then looking for that View under the PageTemplates
folder at the root of the project.
If its not found there, Xperience will also check in the ~/Views/Shared/PageTemplates
folder.
An unhandled exception occurred while processing the request.
InvalidOperationException: The view 'PageTemplates/_MyTemplate' was not found. The following locations were searched:
/Views/KenticoPageBuilderWebPage/PageTemplates/_MyTemplate.en-US.cshtml
/Views/KenticoPageBuilderWebPage/PageTemplates/_MyTemplate.en.cshtml
/Views/KenticoPageBuilderWebPage/PageTemplates/_MyTemplate.cshtml
/Views/Shared/PageTemplates/_MyTemplate.en-US.cshtml
/Views/Shared/PageTemplates/_MyTemplate.en.cshtml
/Views/Shared/PageTemplates/_MyTemplate.cshtml
One interesting thing to note about the error above is that Xperience handles language-specific Page Templates - that is, you can create a unique View for a Page Template for a specific language. Neat!
# Page Content Type not added to Page Builder configuration
Here's an interesting error with an not-so-obvious cause.
An unhandled exception occurred while processing the request.
InvalidOperationException: The view on path '' could not be found.
Kentico.Content.Web.Mvc.Routing.Internal.KenticoRouterWebPageController.Index()
This happens when the Web page Content Type, of the Web page currently being rendered, isn't added to the Page Builder configuration. Since Page Templates depend on the Page Builder (even if they have no <editable-area>
Tag Helpers), they won't work if the Web pages they are rendering aren't Page Builder "enabled".
The fix here is really simple. Find where your builder.Services.AddKentico()
call is (often the Program.cs
file) and add the Web page Content Type to the ContentTypeNames
list.
var builder = WebApplication.CreateBuilder(args);
// ...
builder.Services.AddKentico(features =>
{
features.UsePageBuilder(new PageBuilderOptions
{
// ...
// All Web page Content Types using Page Templates must be in this list!
ContentTypeNames = new[]
{
MyPage.CONTENT_TYPE_NAME
}
});
// ...
});
# Page Builder scripts and styles not rendered on the page
If we've gone through all the scenarios above and made sure our Page Templates are rendering (static markup appears), but the Page Builder UI (ex: <editable-area>
Tag Helpers) is not displaying, our problem might not be with Page Templates, but instead with our Page Builder implementation.
The Page Builder UI is all rendered asynchronously in the browser through JavaScript once the web page has loaded. It has its own design and behavior that doesn't run on the live site.
But, the HTML element hooks for that JavaScript are rendered server-side using some of Xperience's Tag Helpers - for example, the Editable Area Tag Helper.
Tag Helpers only execute if they've been "registered" using the @addTagHelper
directive in Razor, typically in a _ViewImports.cshtml
file. The docs explain which directives to include in our projects, but if we miss this, ASP.NET Core will treat the Tag Helpers in our Page Template Views as normal HTML. This means the client-side JavaScript won't have the right elements to hook into.
Also, _ViewImports.cshtml
files only apply to Views that are siblings of them or in child directories. So, if our Page Template .cshtml
file is located at ~/PageTemplates/Home.cshtml
but our _ViewImports.cshtml
file is located at ~/Views/_ViewImports.cshtml
, it won't apply to the Home.cshtml
file - we need to make sure we have a _ViewImports.cshtml
locally located for all our Razor Views.
I recommend always adding the _ViewImports.cshtml
file for the whole project at the project root - ~/_ViewImports.cshtml
. This way, it applies to all Views, no matter where they are located. I also keep my _ViewStart.cshtml
file at the project root. This is fully supported by ASP.NET Core, will not cause issues in Xperience by Kentico projects, and easily resolves a bunch of problems I've seen developers have.
Another possible cause for the Page Builder UI not displaying is missing <page-builder-scripts />
and <page-builder-styles />
Tag Helpers for the web page being rendered. These are the client-side functionality that makes the Page Builder work.
These could be in the Page Template View, but I prefer to have them in the _Layout.cshtml
to simplify everything. They only render JavaScript/CSS tags when needed, so you don't have to worry about Page Builder UI showing up on random "live site" web pages.