Blog Exposing Color Picker to Content Graph

Exposing Color Picker to Content Graph

OptimizelyContent GraphHeadless
9 May 2025

A guide on how to consume custom CMS property backing types in a headless architecture, using a color picker as an example.

Real life color picker

I must have been living under a rock, unaware of the NuGet package, which adds a configurable color picker to Optimizely CMS. It’s a fantastic add-on that provides editors with an intuitive interface for selecting colors in CMS edit mode.

UI for CMS color picker property.

The package is ready to use after a small amount of configuration. Everything works perfectly, except for one minor detail that can be important in a headless architecture: by default, the selected color is not serialized correctly in Optimizely Graph. This happens because the package introduces a custom backing type for the IColor interface, which is used in content properties:

[ColorPicker]
public virtual IColor? PaletteColor { get; set; }

What’s missing is the registration of a PropertyConverterProvider that can recognize the color property and specify how it should be serialized. To achieve this, we need to create a few classes.

First, we need a simple record or class to contain the data we want to expose in the Graph:

public record PaletteColorModel(string? Name, string? HexCode);

Next, we’ll need to create a PropertyModel that takes the IColor value and converts it into our GraphQL model. This way, we can make sure the color property is mapped and serialized just the way we want when it’s exposed through the Graph.

internal class PropertyPaletteColorModel: PropertyModel<PaletteColorModel, PropertyPaletteColor>
{
    public PropertyPaletteColorModel(PropertyPaletteColor property) : base(property)
    {
        var color = property.Value as IColor;
        Value = new PaletteColorModel(color?.Name, color?.Value);
    }
}

As seen in the generic parameters, they specify the source and output of the mapping. PaletteColorModel is our class, and PropertyPaletteColor is the property backing type from the DoubleJay.Epi.ConfigurableColorPicker namespace.

The next step is to create a converter that initializes the PropertyPaletteColorModel and passes in the property that needs to be serialized.

internal class PropertyPaletteColorModelConverter : IPropertyConverter
{
    public IPropertyModel Convert(PropertyData propertyData, ConverterContext contentMappingContext)
    {
        if (propertyData is not PropertyPaletteColor propertyPaletteColor)
        {
            throw new InvalidOperationException();
        }

        return new PropertyPaletteColorModel(propertyPaletteColor);
    }
}

Last but not least, we need to add a provider. This provider’s job is to make sure that when a color property needs to be serialized, the process is redirected to our custom converter. It connects the property type with the correct serialization logic, ensuring everything works smoothly behind the scenes.

internal class CustomPropertyConverterProvider : IPropertyConverterProvider
{
    public int SortOrder => 200;

    public IPropertyConverter? Resolve(PropertyData propertyData)
    {
        if (propertyData is PropertyPaletteColor)
        {
            return new PropertyPaletteColorModelConverter();
        }

        return null;
    }
}

Lastly, we need to register both the converter and the provider in the DI container:

internal static IServiceCollection AddPropertyConverters(this IServiceCollection services)
{
    services.AddSingleton<IPropertyConverterProvider, CustomPropertyConverterProvider>();
    services.AddSingleton<IPropertyConverter, PropertyPaletteColorModelConverter>();

    return services;
}

It’s worth pointing out that the provider is not specific to the color picker. If there is a need to add another custom property converter, the provider can be used to direct the serialization flow to the appropriate place. In other words, the approach demonstrated here is generic and can be used or extended for any custom property model.

That would be it. Now the color values selected by editors can be successfully exposed through GraphQL and consumed in front-end application. And just like that, we’ve made the internet world a bit more colorful!

References:

https://github.com/jacobjones/DoubleJay.Epi.ConfigurableColorPicker

https://docs.developers.optimizely.com/content-management-system/v1.5.0-content-delivery-api/docs/serialization

More articles

Rubik's Cube as metaphor for hard problems to solve.
OptimizelyHeadlessError

Getting 404 when expecting 401

A short story about the mysterious behavior of an API in headless architecture.

Bunch of decimal numbers
OptimizelyGraphCommerce.NET

Decimal numbers in Optimizely Graph

Storing prices as decimal numbers on a commerce website and planning to expose them through Optimizely Graph? It might not be as straightforward as it seems.

Hand with puzzles
OptimizelySaaSCMSCoveoSearch

Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data from the Optimizely SaaS CMS - all during just a break between coffee refills.

Have a question?

Don't hesitate, and send me an email

smutek.damian95@gmail.com