---
title: List properties
slug: cms-12/docs/property-value-list
docTags: 
createdAt: 2026-08-10T15:55:56.000Z
---

Most property types in Optimizely Content Management System (CMS) can be modeled as lists, including types modeled as block types. For example, given that there is a block type modeled as:

```csharp
[ContentType(AvailableInEditMode = false, GUID = "38d57768-e09e-4da9-90df-54c73c61b270")]  
public class ContactBlock : BlockData {  
   //block properties  
}
```

Then, other Content models can define lists of the block type as:

```csharp
public virtual IList<ContactBlock> Contacts { 
  get; 
  set; 
}
```

![List of content block](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/up7NI_lWGlgtzww9miNqx-20260722-184759.png "list-contact.png")

Each list item looks the same when collapsed – you see just the type name. However, you can override that and specify which property should be used when rendering list item header.

```csharp
[ListItemHeaderProperty(nameof(Blocks.ContactBlock.Heading))]
public virtual IList<ContactBlock> Contacts { 
  get; 
  set; 
}
```

The idea is the same as with the `List<T>`you add the list as a property to your model, and the default descriptors match `IList`  type and render the correct editor. For example:

```csharp
public virtual IList<string> ListOfStrings { 
  get; 
  set; 
}
```

![list of string](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/7SowVdJgs6ntGbEnqxmJh-20260722-184759.png "list-string.png")

```csharp
public virtual IList<int> ListOfIntegers { 
  get; 
  set; 
}
```

![enumerable of int](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/4WwYQ8gLOf-BuyPh8xmtS-20260722-184759.png "list-int.png")

```csharp
public virtual IList<DateTime> ListOfDates { 
  get; 
  set; 
}
```

![list of datetime](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/ltmyyJoVrxdakdU0Hs_96-20260722-184759.png "list-datetime.png")

```csharp
public virtual IList<XHtmlString> ListOfXhtml { 
  get; 
  set; 
}
```

![list of XhtmlString](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/yLLps8NvquztK2C-yf4PO-20260722-184759.png "list-xhtml.png")

You can also define a list property with a custom type inside, which you should not use as a block in the system. You only need an editor and descriptor for a single property, and it is used if the property is used within a list. For example, if there is a custom editor for a type `GeoCoordinate` which is defined like this:

```csharp
public virtual IList<GeoCoordinate> Contacts { 
  get; 
  set; 
}
```

...where `GeoCoordinate` is...

```csharp
public class GeoCoordinate {
  public decimal Latitude {
    get;
    set;
  }
  public decimal Longtitude {
    get;
    set;
  }
  public int Zoom {
    get;
    set;
  }
}
```

...and then there is a custom type descriptor for `GeoCoordinate` which defines a custom editor...

```csharp
[EditorDescriptorRegistration(TargetType = typeof (GeoCoordinate))]
public class GeoCoordinateEditorDescriptor: EditorDescriptor {
  public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes) {
    ClientEditingClass = "alloy/editors/GoogleMapsPicker";

    base.ModifyMetadata(metadata, attributes);
  }
}
```

...`GeoCoordinate` must be inherited from `BlockData` or a custom `BackingType` has to be provided.

```csharp
[ContentType(AvailableInEditMode = false, GUID = "11d57768-e09e-4da9-90df-54c73c61b270")]
public class GeoCoordinate: BlockData {
  public virtual decimal Latitude {
    get;
    set;
  }
  public virtual decimal Longtitude {
    get;
    set;
  }
  public virtual int Zoom {
    get;
    set;
  }
}
```

:::hint{type="info"}
**Note**
The `AvailableInEditMode` attribute and `virtual` modifier are added to all model properties.
:::

You can provide a custom `UIHint` to the list item. For example, you can create a list of images or video files:

```csharp
[UIHint(UIHint.Image)]
public virtual IEnumerable<ContentReference> Images { 
  get; 
  set; 
}
```

...which is rendered like this:

![enumerable of ContentReference](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/Mz67PCtXdSZW6B3B9hbKt-20260722-184759.png "list-image.png")

:::hint{type="info"}
**Note**
The property type does not have to be `IList`. The Optimizely descriptors are configured to match the IList interface but they also cover all base ones, so if you prefer to use `IEnumerable` or `ICollection` you can do so. All the following declarations are matched by the Value List descriptor.
:::

```csharp
public virtual IEnumerable<string> EnumerableStrings {
  get;
  set;
}
public virtual ICollection<string> CollectionOfStrings {
  get;
  set;
}
public virtual IList<string> ListOfStrings {
  get;
  set;
}
```

## Validate a list

You can apply validation to the list itself and the individual items, and you can use `ListItemsAttribute` to control the number of items in the list:

```csharp
[ListItems(5)]
public virtual IList<int> Max5Items { 
  get; 
  set; 
}
```

You can apply the following attributes to individual items:

```csharp
[ItemRangeAttribute(1, 10)]
public virtual IList<int> ItemsBetween1And10 { 
  get; 
  set; 
}
    
[ItemRegularExpression("[a-zA-Z]*")]
public virtual IList<string> LettersOnly { 
  get; 
  set; 
}
    
[ItemStringLength(3)]
public virtual IList<string> ListOfAcronyms { 
  get; 
  set; 
}
```

You can also provide custom metadata and validators to the items inside the list. For example, if there is a `List<ContactBlock>` defined, you can create a metadata extender just for `ContactBlock` or have a custom validator of `ContactBlock` and it is applied to each instance created within the list property.

## Render a list

To render a list:

```csharp
public virtual IList<string> UniqueSellingPoints { 
  get; 
  set; 
}
```

To use the `PropertyFor` helper method, you must define a specific Display template, and to do that, you must first attach a `UIHint` to your property:

```csharp
[UIHint("StringsCollection")]
public virtual IList<string> UniqueSellingPoints { 
  get; 
  set; 
}
```

Then add a `StringsCollection.cshtml` file to the `~/Views/Shared/DisplayTemplates` folder.

You can see an example of a Display Template in the Alloy package:

```csharp
@model IEnumerable<string>
  @if(Model != null && Model.Any())
    {
      <ul>
        @foreach(var stringValue in Model) {
          <li>@stringValue</li>
        }
      </ul>
    }
```

Add a `UniqueSellingPoints` property to your page template:

`@Html.PropertyFor(x => x.CurrentPage.UniqueSellingPoints)`

You will see the following result in the on-page edit:

![UniqueSellingPoints property](https://api.archbee.com/api/optimize/BrbvVORKJ5iIeSMZye5kI/9aLOsvbCQFeYSVLomBzHs-20260722-184759.png "uniquesellingpoints.png")

The same with more complex types:

```csharp
public virtual IList<ContactBlock> ContactBlocks { 
  get; 
  set; 
}
```

The system looks for a partial view for `ContactBlock` inside `~/Views/Shared/DisplayTemplates` and it will render the items as ul -> li by default.

However, you can create a custom display template for the whole list if a simple ul or list is insufficient.
