---
title: UI form component reference extractors
related:
  - https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components/editing-components.md
  - https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components.md
  - https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/widgets-for-page-builder/widget-properties.md
  - https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/widgets-for-page-builder/inline-editors-for-widget-properties.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

The system tracks [usage of content items](https://docs.kentico.com/documentation/business-users/content-hub/content-items.md#track-usage-of-content-items) to help editors determine the impact of modifying content items on the published data. The system tracks all content item references created using the default system form components ([combined content selector](https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components/reference-admin-ui-form-components.md#combined-content-selector), [rich text editor](https://docs.kentico.com/documentation/business-users/rich-text-editor.md)). For all custom [UI form components](https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components.md) that contain links to content item assets or references to content items (e.g., a custom URL selector, a custom rich text inline editor), you need to implement a custom reference extractor.

Additionally, the system [tracks references](https://docs.kentico.com/documentation/business-users/digital-marketing/forms/create-and-edit-forms.md#track-usage-of-forms) to [forms](https://docs.kentico.com/documentation/business-users/digital-marketing/forms.md) from Page Builder component properties. Properties using the [form selector](https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components/reference-admin-ui-form-components.md#form-selector) component are tracked automatically. If you use a custom form component (e.g., based on the [object selector](https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components/reference-admin-ui-form-components.md#object-selector)) to reference forms, you need to mark those properties for form reference tracking. See [Assign form reference tracking to Page Builder component properties](#assign-form-reference-tracking-to-page-builder-component-properties).

## Implement reference extractors

Implement a custom class based on where you want to extract references from:

- [Page and Email Builder component properties](#page-and-email-builder-component-properties-extractors)
- [Content type fields](#content-type-fields-extractors)

### Page and Email Builder component properties extractors

To extract references from Page Builder component properties ([widget](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/widgets-for-page-builder/widget-properties.md), [section](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/sections-for-page-builder/section-properties.md), and [page template](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/page-templates-for-page-builder/page-template-properties.md) properties) and [Email Builder component](https://docs.kentico.com/documentation/developers-and-admins/development/builders/email-builder/develop-email-builder-components.md) properties, implement the `IContentItemReferenceExtractor` interface in a [custom class](https://docs.kentico.com/documentation/developers-and-admins/customization/integrate-custom-code.md). Within the `Extract` method, process the content of the property and return a collection of `ContentItemReference` objects, which contain GUID `Identifier` of content items that are linked in the property.

You need to [register the extractor](#register-reference-extractors) and [assign the extractor](#assign-reference-extractors-to-page-and-email-builder-component-properties) to all properties where references to content items were created using custom form components.

### Content type fields extractors

To extract references from [content type fields](https://docs.kentico.com/documentation/developers-and-admins/customization/field-editor.md), implement the `IFormFieldContentItemReferenceExtractor` interface in a [custom class](https://docs.kentico.com/documentation/developers-and-admins/customization/integrate-custom-code.md). The interface requires you to implement the following methods:

- `Extract` – processes the content of the field and returns a collection of `ContentItemReference` objects, which contain GUID `Identifier` of content items that are linked in the field.
- `CanExtractReferences` – returns a _bool_ value that specifies whether the extractor supports the field type specified in the `FormFieldInfo` parameter. To determine whether a field contains references that can be extracted, use the field's data type (`fieldInfo.DataType` property) and the form control used by the field (`fieldInfo.GetComponentName()` method).

You need to [register the extractor](#register-reference-extractors). After any content item is modified, content type fields of the item are checked against all registered `IFormFieldContentItemReferenceExtractor` implementations and their `CanExtractReferences` methods to determine if references need to be extracted from said fields and the extraction is performed using the `Extract` method of the suitable extractor.

The following example shows the implementation of the default reference extractor used by the system to extract references from rich text fields:

```csharp title="Default extractor for references in the system rich text fields"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

using AngleSharp.Html.Dom;
using AngleSharp.Html.Parser;

using CMS;
using CMS.ContentEngine;
using CMS.Core;
using CMS.FormEngine;

...

public class ContentItemReferenceExtractor : IFormFieldContentItemReferenceExtractor
{
    private const string CONTENT_ITEM_GROUP_NAME = "ContentItemGuid";
    private const string GUID_REGEX = @"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";

    private readonly HtmlParser htmlParser;
    private readonly Regex contentItemLinkRegex = new Regex(@$"getContentAsset\/(?<{CONTENT_ITEM_GROUP_NAME}>{GUID_REGEX})\/{GUID_REGEX}\/", RegexOptions.Compiled | RegexOptions.IgnoreCase);

    public ContentItemReferenceExtractor()
    {
        htmlParser = new HtmlParser();
    }

    // Returns whether the field in the parameter is supported by this extractor
    public bool CanExtractReferences(FormFieldInfo field)
    {
        return string.Equals(field.DataType, FieldDataType.RichTextHTML, StringComparison.InvariantCultureIgnoreCase) ||
            (string.Equals(field.GetComponentName(), RichTextEditorConstants.IDENTIFIER, StringComparison.OrdinalIgnoreCase) &&
                (field.DataType.Equals(FieldDataType.Text, StringComparison.Ordinal) || field.DataType.Equals(FieldDataType.LongText, StringComparison.Ordinal)));
    }


    // Returns a collection of reference object found in the field
    public IEnumerable<ContentItemReference> Extract(object fieldValue)
    {
        if (fieldValue is not string html)
        {
            return Enumerable.Empty<ContentItemReference>();
        }

        var referencesGuids = new HashSet<Guid>();
        var document = htmlParser.ParseDocument(html);

        referencesGuids.UnionWith(document.Images.Where(image => contentItemLinkRegex.IsMatch(image.Source))
            .Select(image => GetContentItemReferenceGuidFromLink(image.Source)));

        var anchors = document.QuerySelectorAll("a").Select(anchors => anchors as IHtmlAnchorElement);

        referencesGuids.UnionWith(anchors.Where(anchor => contentItemLinkRegex.IsMatch(anchor.Href))
            .Select(anchor => GetContentItemReferenceGuidFromLink(anchor.Href)));

        return referencesGuids.Select(guid => new ContentItemReference { Identifier = guid });
    }

    // Converts plaintext links to GUIDs
    private Guid GetContentItemReferenceGuidFromLink(string source)
    {
        return Guid.Parse(contentItemLinkRegex.Match(source).Groups[CONTENT_ITEM_GROUP_NAME].Value);
    }
}
```

> **Tip:** **Universal extractors**
>
> It is generally recommended to use individual extractors for distinct fields or properties. However, as the `IFormFieldContentItemReferenceExtractor` inherits from the `IContentItemReferenceExtractor`, you can create a universal extractor for all fields and properties where the references are stored in the same format.

## Register reference extractors

To register a reference extractor, use the `RegisterImplementation` [assembly attribute](https://docs.kentico.com/documentation/developers-and-admins/customization/integrate-custom-code.md#enable-class-discovery). This attribute ensures the reference extractor is recognized by the system.

```csharp title="Registration of an extractor"
[assembly: RegisterImplementation(typeof(IFormFieldContentItemReferenceExtractor),
                                  typeof(ContentItemReferenceExtractor),
                                  Lifestyle = Lifestyle.Singleton,
                                  Priority = RegistrationPriority.Default)]
```

### Assign reference extractors to Page and Email Builder component properties

After you implement and register an instance of the `IContentItemReferenceExtractor`, you need to assign it to all Page Builder component properties ([widget](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/widgets-for-page-builder/widget-properties.md), [section](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/sections-for-page-builder/section-properties.md), and [page template](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/page-templates-for-page-builder/page-template-properties.md) properties) or [Email Builder component](https://docs.kentico.com/documentation/developers-and-admins/development/builders/email-builder/develop-email-builder-components.md) properties that may contain the corresponding type of references to content items.

Use the `TrackContentItemReference` attribute and specify the type of your extractor as a parameter. Two versions of the attribute are available in different namespaces:

- `Kentico.PageBuilder.Web.Mvc` – for Page Builder component properties
- `Kentico.EmailBuilder.Web.Mvc` – for Email Builder component properties

```csharp title="Properties model"
// Page Builder widget properties model example
public class CustomPageBuilderWidgetProperties : IWidgetProperties 
{
    [CustomEditingComponent]
    [Kentico.PageBuilder.Web.Mvc.TrackContentItemReference(typeof(CustomExtractor))]
    public string PropertyName { get; set; }
}

// Email Builder widget properties model example
public class CustomEmailBuilderWidgetProperties : IEmailWidgetProperties 
{
    [CustomEditingComponent]
    [Kentico.EmailBuilder.Web.Mvc.TrackContentItemReference(typeof(CustomExtractor))]
    public string PropertyName { get; set; }
}
```

### Assign form reference tracking to Page Builder component properties

If you use a custom form component to reference [forms](https://docs.kentico.com/documentation/business-users/digital-marketing/forms.md) (e.g., through the [object selector](https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components/reference-admin-ui-form-components.md#object-selector)), you need to mark the corresponding properties with the `TrackFormReference` attribute from the `Kentico.PageBuilder.Web.Mvc` namespace. The attribute can be applied to properties of type `IEnumerable<ObjectRelatedItem>` or `ObjectRelatedItem`.

> **Info:** Properties of [Page Builder widgets](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/widgets-for-page-builder/widget-properties.md) that use the [form selector](https://docs.kentico.com/documentation/developers-and-admins/customization/extend-the-administration-interface/ui-form-components/reference-admin-ui-form-components.md#form-selector) component are automatically included in [form usage tracking](https://docs.kentico.com/documentation/business-users/digital-marketing/forms/create-and-edit-forms.md#track-usage-of-forms).

> **Note:** Form reference tracking is not supported for properties within Page Builder [sections](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/sections-for-page-builder/section-properties.md) and [page templates](https://docs.kentico.com/documentation/developers-and-admins/development/builders/page-builder/page-templates-for-page-builder/page-template-properties.md).

Unlike content item reference tracking, you do not need to implement a custom extractor.

```csharp title="Properties model"
using Kentico.PageBuilder.Web.Mvc;
using Kentico.Xperience.Admin.Base.FormAnnotations;

// Custom form selector using the object selector
public class CustomFormWidgetProperties : IWidgetProperties
{
    [ObjectSelectorComponent("cms.form", MaximumItems = 1, Label = "Selected form", Order = 1)]
    [TrackFormReference]
    public IEnumerable<ObjectRelatedItem> SelectedForm { get; set; } = new List<ObjectRelatedItem>();
}
```
