---
title: Custom properties
slug: cms-13/docs/custom-properties
docTags: 
createdAt: 2026-07-30T20:22:05.862Z
---

Most common Optimizely Content Management System (CMS) property types have built-in persisting and rendering support. In most cases, creating custom properties is unnecessary. Change the editorial experience without creating a custom property. However, if a custom property is needed, select a proper base type to inherit from, depending on your data type. Create a template for the data type to make the property visible on templates.

When defining a custom property:

- `PropertyDefinitionTypePlugInAttribute` is now sealed and cannot be inherited.
- Use `PropertyDefinitionTypeAttribute` via composition to register your custom property.
- For types directly inheriting from `PropertyData`, you must specify the `DataType` property.

## Use ContentReference\<T> for typed references

`ContentReference<T>` stores references to a specific content type. The generic type ensures compile-time safety and better clarity when the property is bound to a known content type.

For example:

```csharp
public IEnumerable<ContentReference<MyPageType>> LinkedPages { get; set; }
```

The following example implements a custom property by selecting a suitable base type (for example, `PropertyString`, `PropertyNumber`). The property holds an enumerable of `ContentReference` and stores it as a serialized string.

```csharp
[PropertyDefinitionType]
public class LinkingProperty: PropertyLongString {
  public IEnumerable<ContentReference> LinkedReferences {
    get {
      if (!String.IsNullOrEmpty(LongString)) {
        var entries = LongString.Split(';');
        return entries.Select(e => ContentReference.Parse(e));
      }
      return null;
    }
    set {
      LongString = String.Join(";", value.Select(r => r.ToString()));
    }
  }

  public override object Value {
    get {
      return LinkedReferences;
    }
    set {
      IEnumerable<ContentReference> links = value as IEnumerable<ContentReference>;
      if (links != null) {
        LinkedReferences = links;
      } else {
        base.Value = value;
      }
    }
  }

  public override Type PropertyValueType {
    get {
      return typeof (IEnumerable<ContentReference>);
    }
  }

  public override object SaveData() {
    return LongString;
  }
}
```

## PropertyDefinitionType for blocks

Block properties use the common `PropertyDefinitionType`. The block type is now defined using `PropertyDefinition.ItemTypeReference`, rather than directly on the property type.

For example:

```javascript
[PropertyDefinitionType]
public class BlockLinkingProperty : PropertyContentReference
{
    public ItemTypeReference BlockType { get; set; }
}
```

:::hint{type="info"}
**Note**
This standardizes block references across CMS 13 and allows consistent binding in the Visual Builder.
:::

## Index references to other content from a custom property

A soft link indexer ensures data integrity when the custom property stores references to other content instances. When an editor tries to delete a content item, a warning displays if another content item holds a reference to it. If the custom property inherits `PropertyContentReference`, `PropertyUrl`, or `PropertyXhtmlString`, the base type indexer handles soft indexing. The following example shows a soft link indexer for the previous custom property:

```csharp
[ServiceConfiguration(typeof (IPropertySoftLinkIndexer))]
public class LinkingPropertyIndexer: IPropertySoftLinkIndexer<IEnumerable<ContentReference>> {
  public IEnumerable<SoftLink> ResolveReferences(IEnumerable<ContentReference> propertyValue, IContent owner) {
    var softLinks = new List<SoftLink> ();
    foreach(var link in propertyValue) {
      var softLink = new SoftLink {
        OwnerContentLink = owner.ContentLink.ToReferenceWithoutVersion(),
          OwnerLanguage = (owner as ILocalizable)?.Language,
          ReferencedContentLink = link,
          SoftLinkType = ReferenceType.PageLinkReference
      };
      softLinks.Add(softLink);
    }
    return softLinks;
  }
}
```

The indexer registers with the IOC container for the `IPropertySoftLinkIndexer` interface. It implements the generic `IPropertySoftLinkIndexer<T>` interface where the generic argument is the `PropertyValueType` for the property implementation.
