---
title: Develop properties
slug: cms-12/docs/r_p6-properties
docTags: 
createdAt: 2026-08-10T15:56:00.000Z
---

Properties store and present data for content such as pages and blocks. When you define a property on a content type, Optimizely Content Management System (CMS) stores the property as a `PropertyDefinition` class and backs runtime instances of the property type by types inheriting from `PropertyData`. These classes deal with the data inside the property, such as protecting read-only state, preparing data from saving and loading, declaring value type, and so on. `PropertyData` classes also can provide a default user interface.

## Access a property on a read-only page

CMS returns content items in read-only mode from the API. The following code shows how to access a property that resides on the `StandardPage` property on `PageData`:

```csharp
public class StandardPageController: PageControllerBase<StandardPage> {
  public ActionResult Index(StandardPage currentPage) {
    Object propertyValue = currentPage["PropertyName"];
    // do something else and return view
  }
}
```

The accessor determines whether the property with the name `PropertyName` is a property on the page by checking `Property` defined on `IContentData` implemented by `PageData` (which inherits `PageController`). It returns the property value if one exists. If the property is not on the page, a delegate is called to see if the property is found elsewhere. The default delegate looks for the property from the **fetch data from** function (if activated on the current page). The delegate checks the dynamic properties of the page. You can write your own delegate and change this behavior.

## Access a property on a write-enabled page

If the page you are working with is write-enabled, the `Property` accessor returns properties defined on the page; you never get dynamic properties or fetch data from properties when the page is in write mode.

## Access properties on strongly typed content

When you access properties on a strongly typed page, work with that page as you work with an ordinary .NET object. Given a strongly typed page with the following signature:

```csharp
[ContentType]
public class StandardPage: SitePageData {
  public virtual XhtmlString MainBody {
    get;
    set;
  }
}
```

:::hint{type="info"}
**Note**
Why is the property declared as virtual? In the background, a proxy class is created for the page type, where the typed property on your class is connected to the backing `PropertyData` instance. This only works if properties are declared as virtual otherwise they cant be overridden. If the properties are not declared virtual, you need to implement get/set so they read/write data to the underlying property collection instead.
:::

If you are using the generic `EPiServer.Web.Mvc` in your page inheritance, you can access the `MainBody` property like this:

```csharp
public class StandardPageController: PageController<StandardPage> {
  public ActionResult Index(StandardPage currentPage) {
    var mainBody = currentPage.MainBody;
    // do something else and return view
  }
}
```

### Extension methods for strongly typed content

Use the `GetPropertyValue` and `SetPropertyValue` extension methods to take advantage of strongly typed models and also override the get and set behavior for the property. Add a `using` statement referencing the `EPiServer.Core` namespace to your class to use these extension methods.

The following example shows a strongly typed content model that has a fallback behavior for its `PageHeading` property. If the editor sets the `PageHeading` `page` property, the `PageHeading` property on the model returns the set value. If the editor does not set it, the property on the model returns the `PageName` property instead:

```csharp
[ContentType]
    public class StandardPage: SitePageData
    {
      public virtual string PageHeading
      {
        get
        {
          var heading = this.GetPropertyValue(p => p.PageHeading);
     
          // If heading is not set, fall back to PageName
          return String.IsNullOrWhiteSpace(heading) ? PageName : heading;
        }
        set { this.SetPropertyValue(p => p.PageHeading, value); }
      }
    }
```

## Access properties on non-strongly typed content

If a property is unknown or if you want to access several properties (for example, in a loop), you can access the value directly through its object representation.

```csharp
public ActionResult Index(StandardPage currentPage) {
  object propertyValue = currentPage["PropertyName"];
  // do something else and return view
}
```

If you know the type of your value, you can cast it directly.

```csharp
public ActionResult Index(StandardPage currentPage) {
  string propertyValueString = (string) currentPage["PropertyName"];
  // do something else and return view
}
```

If you are unsure of the actual type and want to avoid an exception, assign the property value with the **as** keyword:

```csharp
public ActionResult Index(StandardPage currentPage) {
  string propertyValueAsString = currentPage["PropertyName"] as string;
  if (!String.IsNullOrEmpty(propertyValueAsString)) {
    DoSomething();
  }
  // do something else and return view
}
```

Do not assume the value type for a property type. For example, the values of `PropertyXhtmlString` are of type `XhtmlString`, not `string`. So, casting the value of a `PropertyXhtmlString` to string causes an exception, and using the **as** string operation yields null.

### Use GetPropertyValue for non-strongly typed content

The `EPiServer.Core` namespace has extension methods for `ContentData`. If you import the `EPiServer.Core` namespace (for example, with a **using** statement in code behind), it extends `ContentData` with the `GetPropertyValue` method can simplify property access by reducing the amount of code. For example, use `GetPropertyValue` to get the string representation of a property without first checking against null or to provide a default value if the property is null.

The following usage examples show different overloads of `GetPropertyValue`:

```csharp
public ActionResult Index(StandardPage currentPage) {
  // 1. Get as string, defaults to null and otherwise calls ToString for the value
  string mainBody = currentPage.GetPropertyValue("MainBody");

  // 2. Specify a fallback value
  string mainBodyFallback = currentPage.GetPropertyValue("MainBody", string.Empty);

  // 3. Which is equivalent to
  string mainBodyFallback2 = currentPage.GetPropertyValue("MainBody") ?? string.Empty;

  // 4. So a common usage is probably
  string pageHeading = currentPage.GetPropertyValue("PageHeading", currentPage.PageName);

  // 5. Get typed value, defaults to null if property is not set or defined
  XhtmlString xhtml = currentPage.GetPropertyValue<XhtmlString>("MainBody");

  // 6. Which supports fallback in the same way as the "ToString" overload
  XhtmlString xhtmlWithFallback =
    currentPage.GetPropertyValue<XhtmlString>("MainBody", new XhtmlString());

  // 7. Advanced: Specify a conversion for the value
  // Note: this example is very similar to 1. since we just call ToString, EXCEPT
  // if MainBody is something else than an Xhtml property in which case 1. would still
  // return the ToString representation while this would throw an exception since
  // it tries to cast the value to an XhtmlString before handing it over to the
  // conversion lambda
  string convertedXhtml =
    currentPage.GetPropertyValue<XhtmlString,string>("MainBody", x => x.ToString());

  // 8. This is equivalent to 1. since it treats the property value as object
  string mainBody2 =
    currentPage.GetPropertyValue<object, string>("MainBody", x => x.ToString());

  // do something else and return view
}
```

## Use a property multiple times

If you access a property used multiple times in your code, you can save a reference to the property. Otherwise, the property is fetched from the internal collection for each call, which may also result in several unnecessary calls to the delegate if the property is not native to the page.

For example, use:

```csharp
public ActionResult Index(StandardPage currentPage) {
  PropertyData property = currentPage.Property["PropertyName"];
  if (property != null && !property.IsNull) {
    DoSomething();
  }
  // do something else and return view
}
```

Instead of:

```csharp
public ActionResult Index(StandardPage currentPage) {
  if (currentPage.Property["PropertyName"] != null && !currentPage.Property["PropertyName"].IsNull) {
    DoSomething();
  }
  // do something else and return view
}
```

## Check null values in properties

CMS does not store properties with an empty value in the database. If you access a property from code, it is null—not an empty string, 0, or false. This is because null is a convenient way to check if an editor does not set something or does not exist on this page. You have to compare with null regardless of the data type.

The following example throws a `NullReferenceException` if the value is empty or missing:

```csharp
public ActionResult Index(StandardPage currentPage) {
  StringBuilder sb = new StringBuilder();
  sb.Append(currentPage["PropertyName"].ToString());
  // do something else and return view
}
```

`StringBuilder.Append` accepts null objects, so this example is better than the previous example:

```csharp
public ActionResult Index(StandardPage currentPage) {
  StringBuilder sb = new StringBuilder();
  sb.Append(currentPage["PropertyName"]);
  // do something else and return view
}
```

The following example throws a `NullReferenceException` if the value is empty or missing:

`<%= @ViewModel.CurrentPage.Property["PropertyName"].Value.ToString()%>`

Markup accepts any type and converts to string, so you do not need to cast:

`<%= @ViewModel.CurrentPage["PropertyName"] %>`

The following example shows the fallback for `src` attributes:

`<img src='<%= @ViewModel.CurrentPage["ImageUrl"] ?? "/Missing.png" %/>' />`

To return a string, use the `??` operator as follows:

`string s = currentPage["StringProperty"] as string ?? string.Empty;`

The following example returns a value type:

`DateTime date = (DateTime)(currentPage["DateProperty"] ?? DateTime.Now);`
`int i = (int)(currentPage["IntegerProperty"] ?? 0);`

Another fallback in the markup using the `??` operator as follows:

`<%= currentPage["Heading"] ?? currentPage.PageName %>`

## Update values for a page

If you want to update properties for an existing page, ensure you have a writable version of the page, as shown in the following example.

```csharp
public ActionResult Index(StandardPage currentPage) {
  PageData writablePage = currentPage.CreateWritableClone();
  // do something else and return view
}
```

To set values, assign them directly to the `writablePage` object by working against the properties for the most common page properties or by accessing the values directly:

`writablePage.SomeProperty = "somevalue";`

This is the same as:

`writablePage["SomeProperty"] = "somevalue";`

When you are done with the page, save it by using `IContentRepository` (available in DI container):

`_contentRepository.Save(writablePage, EPiServer.DataAccess.SaveAction.Publish);`

:::hint{type="info"}
**Note**
You cannot save dynamic properties or properties that reside on other pages when you are working against `IContentRepository.Save`.
:::

## HTML encoding and Optimizely CMS page properties

HTML has a special handling for characters such as \< > and symbols ? & /. Some browsers may truncate or corrupt spaces, or problems arise when the browser displays these types of characters. For your website to be XHTML valid, use the HttpUtility.HtmlEncode method on strings that you inject with inline expressions. `HttpUtility.HtmlEncode` is part of .NET Core. The `HtmlEncode` method encodes a string that is displayed in a web browser.

When outputting strings directly in Razor views, normally, you do not need to encode strings. Razor, by default, encodes strings outputted inside a code block. The following example shows how to display the CMS `Name` property:

`<h2>@ViewModel.CurrentPage.Name</h2>`
