Developing page templates

Page templates allow content editors to assign page layouts when creating new pages without requiring developer assistance.

See Using page templates to see the instructions for content editors.

Page templates can also be used for creating pages with structured content (even without any page builder editable areas). This can be useful, for example, if you have multiple possible layouts for product or article pages, and want to allow content editors to choose a suitable layout for each individual page.

Example page template use cases:

  • Creating pages that have a similar structure or content, such as articles or landing pages.
  • Defining the page builder content once, then reusing it for other pages. For example, with the content reordered or modified for the new page.
  • Using different JavaScript, CSS, or _Layout.cshtml files for the pages of the same page type (and the structured content or page builder content defined in it).
  • Using pages with no content, just page builder sections that can be of a different layout for each page template.

Page template types

  • Default page templates are defined in the code of the live site project. Default page templates specify the layout of a page without page content.

  • Custom page templates are created from the page that was created using a default page template. If the page contains page builder content, such as widgets and sections, the custom page template will contain a snapshot of this content on top of the layout defined in the default page template sections. See Saving pages as custom page template.

    Custom page templates do not store structured content

    If the page is using structured content (the data stored in the fields of the page), this content is not saved when saving a page as a custom template.

Developers define the page layout in form of default page templates. Content creators then create a page from a default page template, or define custom page templates that inherit the layout from the default page templates. See Enable content editors to create pages based on page templates.

Enable content editors to create pages based on page templates

  1. Create a page type that supports page templates

  2. Design a default page template

  3. Register the page template within the system

  4. Implement or adjust page template filters on your site.

    Filter page templates only for page types that support page templates.

    If the page template filter is not correctly defined, Xperience will report an error “no available page templates” when creating a new page with any of the page types with the Page builder feature enabled.

Creating page types that support page templates

Create and configure suitable page types and adjust the corresponding logic to use page templates on your pages:

  1. Open the Page types application.

  2. Create a page type with the Page builder and URL features enabled.

    Generate the page type wrapper classes and include them in your project. See Generating classes for Xperience objects for more information.

  3. Modify the controller code depending on your site’s routing mode:

    1. For sites using content tree-based routing:
      1. When using basic routes, all configuration is handled by the system. The system automatically initializes the data context of the page, the page builder feature, and displays the page template used by the page.

      2. When using advanced routes, action methods handling your routes need to return a TemplateResult object. You do not need to provide any parameters to the TemplateResult constructor. The router automatically provides information about the page to be rendered when handling the request.

        Handling page templates under the advanced routing scheme
        
        
        
                 public ActionResult Index()
                 {
                     // Custom processing logic
        
                     // Leverages information provided by the router when serving the request
                     // to retrieve the corresponding page. No need to specify the page to render.
                     return new TemplateResult();
                 }
        
        
        
         
    2. For sites using custom routing:
      1. Edit () the created page type and set its URL pattern.
      2. In your live site’s code, create a controller or view component with a GET action that handles the rendering of the pages:
        • The GET action must return a TemplateResult object.

        • Pass the identifier of the page (TreeNode.DocumentID property) or the entire TreeNode object of the rendered page to the TemplateResult constructor. If you have manually initialized the page data context for the current page, you can use the parameterless TemplateResult() constructor. 

          Handling page templates under custom routing
          
          
          
                    private readonly IPageRetriever pagesRetriever;
          
                    // Gets instances of required services using dependency injection
                    public PageTemplateCustomRoutingInitialization(IPageRetriever pagesRetriever)
                    {
                        this.pagesRetriever = pagesRetriever;
                    }
          
                    /// <summary>
                    /// A GET action displaying a page where you wish to use page templates.
                    /// </summary>
                    /// <param name="pageAlias">Page alias of the displayed page.</param>
                    public ActionResult Index(string pageAlias)
                    {
                        // Retrieves a page from the Xperience database
                        TreeNode page = pagesRetriever.Retrieve<TreeNode>(query => query
                                            .Path("/Landing-pages", PathTypeEnum.Children)
                                            .WhereEquals("NodeAlias", pageAlias)
                                            .TopN(1))
                                            .FirstOrDefault();
          
                        // Responds with the HTTP 404 error when the page is not found
                        if (page == null)
                        {
                            return HttpNotFound();
                        }
          
                        // Returns a TemplateResult object, created with the retrieved page
                        // Automatically initializes the page data context and the page builder feature
                        // for all editable areas placed within templates
                        return new TemplateResult(page);
                    }
          
          
          
            
  4. Design the default page template.

  5. Register the default page template in the system.

    If you create a page based on the default page template before the template is registered in the system will cause an error when attempting to view the page.

    Delete the page and create it again after the page template is registered in the system.

  6. Create a page template filter to filter the created page templates only for page types that support page templates specific pages.

A default page template is created. Content editors can use the page template to create new pages.

Designing default page templates

On a basic technical level, page templates are HTML pages. The main step in the development of a page template is to create a full page view that defines the output.

Within the MVC architecture, the page template view is served by a controller and a model is used to pass any required data. In many cases, templates use a default controller and view model provided by the Xperience API.

In both cases you can develop page templates with properties, which allow content editors to customize the template appearance in the administration interface. For templates with configurable properties, you need to create an additional model class that represents the properties and passes their values to the controller. See Defining page template properties to learn more.

MVC Areas

Page templates are designed to be used in the global scope and their code files must be placed in the application root of your MVC project (not in an MVC Area). Creating page templates in MVC Areas may lead to unexpected behavior.

Basic page templates

Use the following process to develop a page template:

  1. Create a view with code that defines the output of the page template according to the general MVC best practices.
    • The output must be a full HTML page, so the view must include the following:

      • Full HTML markup, including the html, head and body elements
      • Links to all necessary resources, such as stylesheets and scripts
      • Links to page builder scripts and styles
    • Use MVC layouts with the template view for any shared output code (based on your requirements, you can use your site’s main layout, a dedicated layout for page templates, etc.).

    • We recommend storing page template views in the ~/Views/Shared/PageTemplates folder, and using a view name that matches the identifier assigned to the template upon its registration prefixed with the underscore (‘_’) character. Replace any period characters (‘.’) in the identifier with underscores (‘_’) in your view name. Alternatively, you can use any required view location or name, and then specify it when registering the template.

    Accessing the template’s page

    If you need to work with the data of the page using the currently processed page template, use the ComponentViewModel class as the view’s model and access its Page property. The property returns a TreeNode object representing the given page. If you need to load values from the fields of a specific page type, you can convert the TreeNode object to an instance of a specific page type wrapper class (the page using the template must then be of the given page type).

  2. Register the page template into the system. See Registering page templates.

With this approach, the template’s view is automatically displayed using a default controller provided by the Xperience API. The values of any properties defined for the template can be passed to the view by using the default ComponentViewModel<TPropertyModel> class as the model.

Example of page template development

To see a scenario with full code samples which will guide you through the process of developing a simple template, visit Example - Developing a page template with a configurable property.

Page templates with a custom controller

When developing page templates with advanced functionality, you may need to take full control over the template’s logic. You can do this by implementing the template’s controller and view model, in addition to the view. This allows you to run any custom code within the template’s controller, pass any type of required data to the view, or even switch between completely different views based on the current scenario.

The following steps describe the advanced development process for page templates:

  1. Create a controller class for the page template.

    • We recommend storing template controllers in the ~/Controllers/PageTemplates folder.
  2. Make the controller inherit from the PageTemplateController base class (available in the Kentico.PageBuilder.Web.Mvc.PageTemplates namespace).

  3. Implement the default Index action in the controller, which is used to retrieve the template markup. The action must return the page template’s HTML content, typically a view.

    Notes

    • Do not disable POST requests for the Index action (e.g., by using the HttpGet attribute). POST requests to the Index action are used in the page builder feature.
    • Template controller actions used to retrieve the markup cannot be asynchronous (cannot use the async function declaration). Actions that render template markup are called as child actions when rendering the markup of a page, but MVC 5 does not support asynchronous child controller actions.
  4. Create any required view model classes used to pass data from the template controller to the view.

    • We recommend storing template models in the ~/Models/PageTemplates/<template name> folder.

    Referencing actions of page template controllers

    When using methods that reference page template controller actions within the code of the given view template (e.g. RedirectToAction in the controller or Html.ActionLink in views), you need to explicitly specify the controller name as a parameter of the method. For example, use RedirectToAction(actionName, controllerName) instead of RedirectToAction(actionName).

  5. Prepare a view that defines the output of the page template according to the general MVC best practices.

    • The output must be a full HTML page, so the view must include the following:

      • Full HTML markup, including the html, head and body elements
      • Links to all necessary resources, such as stylesheets and scripts
      • Links to page builder scripts and styles
    • Use MVC layouts with the template view for any shared output code (based on your requirements, you can use your site’s main layout, a dedicated layout for page templates, etc.).

    • We recommend storing page template views in the ~/Views/Shared/PageTemplates folder.

  6. Register the template into the system. See Registering page templates.

With this advanced development approach, you have full responsibility and control over the template’s controller, view model, and view.

Accessing the data of the current page

If you need to access fields of the page containing the currently processed page template, obtain an instance of the IPageDataContextRetriever service (we recommend using dependency injection) and call its Retrieve<TPageType> method. Specify either a TreeNode object or a page type wrapper class as the generic parameter. The method returns an IPageDataContext<TPageType>object with the following properties:




// Contains an instance of the IPageDataContextRetriever service (e.g., obtained via dependency injection)
private readonly IPageDataContextRetriever pageDataContext;

// Gets the page of the Article page type where the currently processed page template is placed
var article = pageDataContext.Retrieve<Article>().Page;


Accessing the data of the current page in POST actions

Data of the current page rendered using the page template are by default not accessible in controller actions that handle POST requests. Such requests do not contain sufficient information to identify the page from which they originate. 

To access page data in POST actions, you need to include information about the current page into the data submitted by the corresponding form in the page template’s output – call the Html.Kentico().PageData extension method within the given form tag in your page template view.

Example



using System.Web.Mvc.Ajax
using Kentico.Web.Mvc
using Kentico.PageBuilder.Web.Mvc

...

@using (Ajax.BeginForm("PostAction", "CustomPageTemplate", null, new AjaxOptions
{
    HttpMethod = "POST",
    UpdateTargetId = "pageTemplateForm"
}, new { id = "pageTemplateForm" }))
{
    @Html.Kentico().AntiForgeryToken()
    @Html.Kentico().PageData()
    ...
    <input type="submit" value="Submit" />
}


The method renders a hidden form field that persists information about the current page. The page data can be retrieved via IPageDataContextRetriever.Retrieve<TPageType> in the corresponding POST action.

You can also develop page templates with properties, which allow content editors to customize the template appearance in the administration interface. For templates with configurable properties, you need to create an additional model class that represents the properties and passes their values to the view component. See Defining page template properties to learn more.

Areas

Page templates are designed to be used in the global scope and their code files must be placed in the application root of your project (not in an Area). Creating page templates in Areas may lead to unexpected behavior.

Use the following process to develop a page template:

  1. Create a view with code that defines the output of the page template according to general MVC best practices.
    • The output must be a full HTML page, so the view must include the following:

      • Full HTML markup, including the html, head and body elements
      • Links to all necessary resources, such as stylesheets and scripts
      • Links to page builder scripts and styles
    • Use MVC layouts with the template view for any shared output code (based on your requirements, you can use your site’s main layout, a dedicated layout for page templates, etc.).

    • We recommend storing page template views in the ~/PageTemplates folder, and using a view name that matches the identifier assigned to the template prefixed with the underscore (‘_’) character.

    Accessing the template’s page

    If you need to work with the data of the page using the currently processed page template, use the ComponentViewModel class as the view’s model and access its Page property. The property returns a TreeNode object representing the given page. If you need to load values from the fields of a specific page type, you can convert the TreeNode object to an instance of a specific page type wrapper class (the page using the template must then be of the given page type).

  2. Register the page template into the system. See Registering page templates.

With this approach, the template’s view is automatically displayed using logic provided by the Xperience API. The values of any properties defined for the template can be accessed in the view by using the ComponentViewModel<TPropertyModel> class as the model.

Adding custom business logic

The basic implementation of a page template consists of only a view file (and possibly a properties class). You may need additional business logic, for example if you need to:

  • respond to the configuration of the template’s properties
  • perform interactions based on the page where the template is rendered
  • execute general business logic not suitable for views (e.g., database operations)

For the purpose of storing the business logic your templates require, we recommend creating dedicated service or view component classes. See the following sections for details:

Example of page template development

To see a scenario with full code samples which will guide you through the process of developing a simple template, visit Example - Developing a page template with a configurable property.

Service class

Separate your logic into a dedicated service class when you need to reflect the configuration of the template’s properties, the context of its page, or execute general business logic not suitable for views (e.g., database operations). You can then inject the service to the template’s view and call your code as required.

Use the following general process to implement a service class for a page template:

  1. Create the service class for your page template.

    • We recommend storing the service class in the ~/PageTemplates/<TemplateName> directory together with other files required by the page template. For reusable code shared across components, you can create a ~/PageTemplates/Shared directory.
  2. Implement required logic within the service class. For example, you may wish to react to the configuration of the template’s properties, or the fields of the page where the template is rendered.

    • The template’s properties (if implemented) are stored in the template’s model (ComponentViewModel<TPropertiesType>).
    • The current page can be accessed via the model’s Page property.
  3. Register the service within the application’s service container.

  4. Inject the service into the template’s view file using the @injectdirective.

    
    
    
     @inject Mysite.MyTemplateService templateService
    
    
     
  5. Call the service as required.

Using this approach, you can separate business and view-layer code.

View component

View components can be used when you have reusable rendering logic that needs to interact with the application’s business layer.

Use the following general process to implement a view component for a page template:

  1. Create a view component and its partial view.
    • We recommend storing view components related to the page template in the /PageTemplates/<TemplateName>directory together with other related files. For reusable code shared across components, you can create a /PageTemplates/Shared directory.
  2. Implement the component’s InvokeAsync method. You can pass any required parameters from the template’s view.
    • The template’s properties (if implemented) are stored in the template’s model (ComponentViewModel<TPropertiesType>).
    • The current page can be accessed via the model’s Page property.
  3. Invoke the component from within the template’s view.

Using this approach, you can decouple business and view-layer code.

Handling POST actions

If your page template needs to communicate with the server using POST actions, create a custom controller class containing the required methods and logic. We recommend storing the class in the ~/PageTemplates/<TemplateName> folder together with other files required by the page template.

Data of the current page rendered using the page template is by default not accessible in controller actions that handle POST requests. Such requests do not contain sufficient information to identify the page from which they originate. 

To access page data in POST actions, you need to include information about the current page into the data submitted by the corresponding form in the page template’s output – call the Html.Kentico().PageData extension method (or the method’s Tag Helper alternative) within the given form tag in your page template view.




@using Kentico.Web.Mvc
@using Kentico.PageBuilder.Web.Mvc

<form asp-controller="TemplateController" asp-action="HandlePost" id="form">

    ...

    @Html.Kentico().PageData()

    <input type="submit" value="Submit" />
</form>


The method renders a hidden form field that persists information about the current page. The page data can be retrieved via the IPageDataContextRetriever service in the corresponding controller action. 

Obtain an instance of the IPageDataContextRetriever service (using dependency injection) and call its Retrieve<TPageType> method. Specify either a TreeNode object or a page type wrapper class as the generic parameter. The method returns an IPageDataContext<TPageType> object that contains the current page object in its Page property. You can also access the metadata and evaluate the permissions and authentication requirements of the page via the object’s Metadata and Security properties.




// Holds an instance of the IPageDataContextRetriever service (e.g., obtained via dependency injection)
private readonly IPageDataContextRetriever pageDataContext;

// Gets the page of the Article page type where the page template is placed
var article = pageDataContext.Retrieve<Article>().Page;


Registering page templates

Every page template needs to be registered into the system to be available. Register templates using the RegisterPageTemplate assembly attribute (available in the Kentico.PageBuilder.Web.Mvc.PageTemplates namespace).

To register Basic page templates (without a custom controller class), we recommend adding the assembly attributes to a dedicated code file. For example, you can create a file named PageBuilderComponentRegister.cs in your project’s ~/App_Start folder and use it to register your page builder components. For basic page templates, specify the following attribute parameters:

  • Identifier – the unique identifier of the template. We recommend using a unique prefix in your template identifiers to prevent conflicts when deploying templates to other projects, for example matching your company’s name.

  • Name – the name used to identify the template when displayed in the administration interface.

  • PropertiesType – only required for templates with properties. Specifies the System.Type of the template’s property model class.

  • (Optional) CustomViewName – specifies the name and location of the view that defines the template’s output. If not set, the system searches for a corresponding _<Identifier>.cshtml view in the ~/Views/Shared/PageTemplates folder (any period characters ‘.’ in the identifier are replaced by underscores ‘_’).

    Basic template registration example
    
    
    
      [assembly: RegisterPageTemplate("CompanyName.MyTemplate", "My template", typeof(CustomTemplateProperties), "PageTemplates/_MyTemplate")]
    
    
      

For templates with a custom controller, you can add the assembly attribute directly into the controller code file (above the controller class). In this case, specify the following attribute parameters:

  • Identifier – the unique identifier of the template. We recommend using a unique prefix in your template identifiers to prevent conflicts when deploying templates to other projects, for example matching your company’s name.

  • ControllerType – the System.Type of the template’s controller class.

  • Name – the name used to identify the template when displayed in the administration interface.

    Controller template registration example
    
    
    
      [assembly: RegisterPageTemplate("CompanyName.MyTemplate", typeof(MyTemplateController), "My template")]
    
    
      

When registering any type of page template, you can also set the following optional attribute properties:

  • Description – the description of the template displayed as a tooltip.
  • IconClass – the font icon class displayed as a thumbnail when selecting templates.



[assembly: RegisterPageTemplate("CompanyName.MyTemplate", typeof(MyTemplateController), "My template", Description = "This is a custom template.", IconClass="icon-l-img-3-cols-3")]


Every page template needs to be registered into the system to be available. Register templates using the RegisterPageTemplate assembly attribute (available in the Kentico.PageBuilder.Web.Mvc.PageTemplates namespace).

If a page template does not require any additional services or view components, we recommend adding the registration attributes to a dedicated code file. This keeps your registrations organized. For example, you can create a file named PageTemplateRegister.cs in your project’s ~/PageTemplates folder and use it to register page templates. 

When registering a page template, specify the following attribute parameters:

  • Identifier – the unique identifier of the template. We recommend using a unique prefix in your template identifiers to prevent conflicts when deploying templates to other projects, for example matching your company’s name.

  • Name – the name used to identify the template when displayed in the administration interface.

  • PropertiesType – only required for templates with properties. Specifies the System.Type of the template’s property model class.

  • CustomViewName – specifies the name and location of the view that defines the template’s output. If not set, the system searches for a corresponding _<Identifier>.cshtml view in the ~/Views/Shared/PageTemplates folder (any period characters ‘.’ in the identifier are replaced by underscores ‘_’).

    Template registration example
    
    
    
      [assembly: RegisterPageTemplate("CompanyName.MyTemplate", "My template", typeof(CustomTemplateProperties), "~/PageTemplates/_MyTemplate.cshtml")]
    
    
      

You can also set the following optional properties:

  • Description – the description of the template displayed as a tooltip.
  • IconClass – the font icon class of the icon displayed as a thumbnail when selecting templates.

Localizing template metadata

To allow content editors to experience the page builder in their preferred UI culture, you can localize the Name and Description values of page templates.

Storing files for template-based pages

We recommend storing files for pages that utilize page templates in media libraries. Media library files are not bound to specific pages and their content is reusable (as opposed to page attachments). Custom page templates store only the configuration of pages, e.g. for multimedia files only the file identifiers are stored and not the files themselves. As a result, if you create a custom template from a page that displays a file, new pages created with this template will display the file only if it is stored in a media library.

Adding scripts and styles for page templates

To add JavaScript and CSS styles required by your page templates, we recommend placing script and stylesheet files into sub-folders under the ~/Content/PageTemplates directory of your MVC project (you may need to create the PageTemplatesdirectory). You can use sub-folders that match the identifiers of individual templates, or a Shared sub-folder for assets used by multiple templates.

The system does not automatically include or create bundles for .js and .css page template files. You need to include and link all custom scripts and styles for your page templates manually.

CSS notes

  • Only use the ~/Content/PageTemplatesdirectory to add basic styles that are required for the template to render correctly. Any site-specific styles that finalize the live site design of the template should be handled separately within the given site’s main stylesheet.
  • To avoid potential conflicts between styles from other third-party components, we recommend adding a unique prefix to your CSS classes and identifiers (for example #CompanyName-mid-column), or employ similar measures to ensure their uniqueness.

When adding JavaScript and CSS styles required by your page templates, we recommend placing script and stylesheet files into sub-folders under:

  • ~/wwwroot/PageBuilder/Public/PageTemplates/<PageTemplate> – scripts and styles intended for the live site.
  • ~/wwwroot/PageBuilder/Admin/PageTemplates/<PageTemplate> – scripts and styles intended for the administration interface (when working with the page template in the page builder editing interface).

You can use sub-folders that match the identifiers of individual templates, or a Shared sub-folder for assets used by multiple templates. Note that this recommendation only applies when using the default configuration of the bundling support provided by Xperience and may be different for your project. See Bundling static assets of builder components .

CSS notes

  • Only use the specified directories to add basic styles that are required for the template to render correctly. Any site-specific styles that finalize the live site design of the template should be handled separately within the given site’s main stylesheet.
  • To avoid potential conflicts between styles from other third-party components, we recommend adding a unique prefix to your CSS classes and identifiers (for example #CompanyName-mid-column), or employ similar measures to ensure their uniqueness.