---
title: Page types and templates
slug: cms-11/dev-docs/content-types
docTags: 
createdAt: 2026-07-22T19:45:22.987Z
---

:::hint{type="info"}
**Note**
The examples here are based on MVC.
:::

Optimizely Content Management System (CMS) relates pages, page types, and page templates in the following ways:

- A *page type* defines a set of *properties*, for example, page name and publish date.
- A *page* is an instance of the .NET class defining the page type. When you create a page in edit view, values are assigned to the properties and stored in the database.
- The *controller* and *view* fetch the stored property values and render the output.
- A *template* can be associated with the page type to render the output in a certain context.

In the following, you will find examples of how to work with page types and associated controllers, views, and templates for rendering the content.

:::hint{type="info"}
**Note**
Standard routing is not registered by default for MVC, but if it is used, renaming of a controller or action will affect the URL/route to the action.
:::

## Page types

In Optimizely Content Management System (CMS), you usually define page types in code as classes based on a model inheriting from `EPiServer.Core.PageData`. A `PageData` object is the programmatic representation of a page containing the properties defined in your .NET class. The value of `currentPage` is automatically set to the `PageData` object that the client requests. During initialization, CMS scans the `bin` folder for .NET classes inheriting `PageData` and creates a page type for each of the classes found. CMS creates a corresponding property on the page type for public properties on the class.

### Create a page type

Using the [Visual Studio integration](https://marketplace.visualstudio.com/items?itemName=EPiServer.EpiserverCMSVisualStudioExtension), you create a page type by adding the `Page type` item to the `Pages` subfolder under `Models` in your project. See [Get started with Content Cloud (CMS 11)](docId\:D4hWGQLetoJnFv_cMIII1).

The abstract `SitePageData` base class inherits from `EPiServer.Core.PageData`. This base class has an SEO property for pages inheriting from the class.

**Example:** A simple article page type with a MainBody editor area of the property type `XhtmlString`, inheriting from the `SitePageData` base class.

```csharp
using System;
using System.ComponentModel.DataAnnotations;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAnnotations;
using EPiServer.SpecializedProperties;

namespace MyEpiserverSite.Models.Pages {
    [ContentType(DisplayName = "ArticlePage", GUID = "b8fe8485-587d-4880-b485-a52430ea55de", Description = "Basic page type for creating articles.")]
    public class ArticlePage: SitePageData {
      [CultureSpecific]
      [Display(
        Name = "Main body",
        Description = "The main body editor area lets you insert text and images into a page.",
        GroupName = SystemTabNames.Content,
        Order = 10)]
      public virtual XhtmlString MainBody {
        get;
        set;
      }

    }
  }

  **
  Example: ** The SitePage base class, with the SEO property.

namespace MyEpiserverSite.Models.Pages {
  public abstract class SitePageData: EPiServer.Core.PageData {
    [Display(GroupName = "SEO", Order = 200, Name = "Search keywords")]
    public virtual String MetaKeywords {
      get;
      set;
    }
  }
}
```

CMS automatically generates a unique GUID for the page type when creating a page type using the Visual Studio extension. Also, note that page types implicitly contain a set of *built-in properties* available for pages, regardless of the instance. See `PageData.Property` for built-in properties.

Because CMS has not created the rendering, you cannot edit pages based on this page type from the **On-Page** edit view; only from the **All Properties** edit view.

:::hint{type="info"}
**Note**
Why are the properties declared as *virtual* here? What happens in the background is that a proxy class is created for the page type, and data is loaded from the database to a property carrier (`Property`), receiving the data. Through `Castle` (Inversion of Control tool), the properties in the proxy page type will be set, and this only works if properties are declared as virtual. If the properties are not declared virtual, you need to implement get/set so that these will read/write data to the underlying property collection instead.
:::

## Page controllers and views

Models, controllers, and views in MVC clearly separate data, business logic, and presentation. The *controller* contains the business logic, handles URL requests, and selects the *view*, a visual data model presentation. In MVC, the controller is the registered class that supports specific page types, and you can regard it as the *template*. The template defines which content types it can render in a specific context.

### Create a controller and a view

Using the Optimizely Visual Studio extension, you create a controller by adding a CMS item of type *Page Controller (MVC)* to the `Controllers` folder in your project. Your controller should inherit from `EPiServer.Web.Mvc.PageController<T>`, where T is your page type. If it chooses the renderer for the page type, it calls this controller for the page type.

To add the corresponding view for the controller, create a subfolder under `Views` and add a CMS item of type `Page View (MVC)`. Or, click inside the controller to add the view. Ensure you follow the standard *naming conventions* in MVC for your model, controllers, and views.

A `PageControllerBase` class reuses logic for multiple pages, which holds the logic for a logout action, and inherits from `PageController`. You can also add a [view model](docId\:LgTCqyJH2ga8yBJ2DAbNM) to more than just page objects to the views.

Use [HTML helpers](docId:2v4W6kW44Y-EIu5pSoHML) in MVC to render properties, such as `Html.PropertyFor`, which renders property values based on their property type.

**Example:** The controller for displaying the Article page type, inheriting from `PageControllerBase`.

```csharp
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using EPiServer;
using EPiServer.Core;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Web.Mvc;
using MyEpiserverSite.Models.Pages;
using System.Web.Security;

namespace MyEpiserverSite.Controllers {
  [TemplateDescriptor(Default = true)]

  public class ArticlePageController: PageControllerBase<ArticlePage> {
    public ActionResult Index(ArticlePage currentPage) {
      // Implementation of action view the page. 
      return View(currentPage);
    }
  }
}
```

**Example:** The page controller base, inheriting from `PageController`, and with `SitePageData` as generic type.

```csharp
namespace MyEpiserverSite.Controllers {
  public abstract class PageControllerBase<T>: PageController<T> where T: SitePageData {
    // Providing a logout action for the page.
    public ActionResult Logout() {
      FormsAuthentication.SignOut();
      return RedirectToAction("Index");
    }
  }
}
```

**Example**: The corresponding rendering view displays the Article page.

```csharp
@using EPiServer.Core
@using EPiServer.Web.Mvc.Html

@model MyEpiserverSite.Models.Pages.ArticlePage 
<h1>@Html.DisplayFor(m => m.Heading)</h1>

  <h3>@Html.PropertyFor(m => m.Introduction)</h3>

  <div >
  @Html.PropertyFor(m => m.MainBody) 
  </div>
```

With the added rendering using `Html.PropertyFor`, the property is now also editable in the **On-Page** editing view.

![](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/kZi6aUPAqSzMFJh9pP8dy-20260722-194627.png "pagetypetemplates.png")

## Use HTML Helpers

You can also use other specific CMS HTML helpers as an alternative to `Html.PropertyFor`. There are helpers for rendering links, content areas, translations, and navigation. You call these HTML helpers through `Html.DisplayFor`, but you can also use them directly.

## Use templates

Templates define how you render content. The template (controller) selected to render a content instance depends on the context. Use the `TemplateDescriptor` attribute to add metadata and define default templates, and Tags to define which template to use. Based on this information and any defined **display channels** and **display options** settings, the TemplateResolver decides which template to use in a specific context.

### Template registration and routing

Page types and controllers created from code using the CMS Visual Studio integration are strongly typed and inherit from CMS base classes to ensure that the templates used are automatically registered as supported for the specified page type. See [Rendering](docId:1j5tkfTPZbGw5lRhAh4Xx) for information.
