setDefault vs require in CML: Pre-Select Products Users Can Remove

Let’s picture: a user opens the Advanced Configurator and finds a product already added for them. They remove it, move on – and a moment later it is back. They remove it again. Again it returns. From the user's side it looks broken. From the model's side, everything is working exactly as written.

That gap – between “selected for you” and “selected for you, and you may decide otherwise” – used to be hard to express in CML. 

Until Summer ’26, there was no clean way to pre-select an optional product and still let the user opt out. This article walks through the rules that get you there, where each one quietly fails, and the one distinction that decides which to reach for.

Summary

  • Pre-selecting an optional product that a user can then unselect – and it sticks – is a Summer '26 capability. Earlier approaches either could not target a subtype or would force the product back in.
  • default, preference, require, and setDefault all “select something for the user,” but only setDefault recommends without enforcing.
  • The core distinction: require enforces the expression every time its condition is true, setDefault only acts when its condition changes, and otherwise stays passive – that makes the user's unselection persist.

The problem: recommend a product, but let the user say no

Pre-selecting a required product is easy. The interesting case is the optional one: you want a product in the bundle by default, because most users want it, but you also want a user who removes it to keep it removed.

To make this concrete, the rest of the article uses a small book bundle. There is a base Book type, a BiographyBook subtype, and two concrete titles underneath it. The bundle holds up to five books:

type Book;

type BiographyBook: Book;

type TheRiversDaughter: BiographyBook;

type BeReadyWhenTheLuckHappens: BiographyBook;

 

type BookBundle {

    relation Books: Book[0..5];

}

The goal is simple to state and, as it turns out, not simple to achieve before Summer '26: open the configurator with a biography book already added, and let the user remove it for good if it is not what they want.

Approach 1: the default function

The first instinct is the default function on a relation. It adds a specific product when the configurator opens:

type BookBundle {

    relation Books: Book[0..5] {

        default BeReadyWhenTheLuckHappens(1);

    }

}

This works – with one constraint. default needs a final type, one that has products associated with it. It cannot point at an abstraction. So you can pre-select BeReadyWhenTheLuckHappens, but you cannot say “add any biography book.” The moment the requirement is “pre-select something from the BiographyBook family, doesn't matter which,” default is out.

Approach 2: preference (and why the product keeps coming back)

The next candidate is preference, which can reason about a subtype:

type BookBundle {

    relation Books: Book[0..5];

    preference(Books[BiographyBook] > 0);

}

This selects the first biography book in the domain, which looks right at first. The problem appears the moment the user removes it. preference re-enforces the selection, so the book reappears. The user unselects; the engine reselects. That is the “product is selected back” loop – the model is technically satisfying the rule, and the user has no way to opt out.

This is the wall most teams hit. default is too specific, preference is too insistent, and neither leaves room for “recommended, but optional.”

Approach 3: setDefault – recommend without enforcing

setDefault is the rule that fills the gap. It adds a biography book when the configurator starts, and then steps back:

type BookBundle {

    relation Books: Book[0..5];

    setDefault(true, Books[BiographyBook]);

}

Now the bundle opens with a biography book selected, the user can remove it, and it stays removed. The condition here is simply true, so the recommendation applies once at the start and does not fight the user afterward. This is the behavior the earlier approaches could not produce.

Tying the recommendation to a condition

A permanent recommendation is often not what you want. More often the product should be suggested only under certain circumstances – a region, a customer tier, a previously selected option. setDefault takes a condition as its first argument:

type BookBundle {

    relation Books: Book[0..5];

    setDefault(BookRegion == 'NA', Books[BiographyBook]);

}

This adds a biography book (the first in the domain) when BookRegion is NA. The user can still unselect it. And here is the behavior that defines the rule: if BookRegion later changes away from NA, the book is unselected; and if it is set back to NA, the book is recommended again. The rule reacts to the change in its condition, not to the condition's standing value.

The optional message – and its catch

setDefault accepts a third argument: a message shown when the condition is not satisfied, to remind the user that the product is still recommended even though it is not enforced.

type BookBundle {

    relation Books: Book[0..5];

    setDefault(BookRegion == 'NA', Books[BiographyBook] == 1, "Only one biography book is recommended!");

}

It is useful, but read the behavior carefully before you rely on it. The message fires whenever the expression is not satisfied – including when the user has more than one biography book selected. That may not be the nudge you intended, so treat the message as a hint, not a guardrail.

Approach 4: require – when you do want to enforce it

Sometimes the right answer is to enforce the selection. That is what require rule is for:

type BookBundle {

    relation Books: Book[0..5];

    require(BookRegion == 'NA', Books[BiographyBook]);

}

As soon as BookRegion is NA, a biography book is selected – and the user cannot unselect it until BookRegion changes. In this particular setup, preference behaves the same way require does: both keep forcing the selection back.

How setDefault and require differ in practice

Strip away the examples and the difference between the two rules comes down to one line:

The setDefault rule attempts to satisfy the expression only when its condition is changed. If nothing changes, setDefault performs a passive evaluation. The require rule always attempts to satisfy the expression while the condition is true.

From here, the key distinctions are easy to read off.

require is a standing instruction – true means enforced, continuously. setDefault is a one-time reaction to change – it acts at the edge, when the condition flips, and is silent in between. The passive evaluation is precisely why a user's unselection survives under setDefault and gets overwritten under require.

Behavior setDefault require
When does it act? Only when the condition changes Continuously, while the condition is true
Can the user unselect the result? Yes – and it stays unselected No – forced back until the condition changes
Targets a subtype (e.g. BiographyBook)? Yes Yes
Best for Recommending an optional product Enforcing a mandatory one
Mental model Suggested, you decide Required while this holds

The decision rule is short: if the user must be able to opt out, use setDefault. If the selection has to hold whenever the condition is true, use require.

A setDefault limitation with generic types

There is currently a bug with generic (non-final) types. When a rule targets an abstraction like BiographyBook rather than a concrete title, the engine latches onto the first final type in the domain and only reasons about that one. This was observed on platform version 262.10.2 and may change in later releases.

The practical consequence: even when a biography book is selected, if it is not the first in the domain, you can still see a message saying the setDefault condition could not be satisfied. The selection is fine – the message is wrong.

The honest takeaway is that this functionality is new, and new functionality has edges. Which leads to the one recommendation that matters more than any single rule.

Final thoughts

The most useful advice here does not come from the documentation. Try every way of selecting a product, then try to break each one with an approach the model does not expect – and see whether it holds. The four rules look interchangeable on a slide and behave differently in a live configurator, so the only reliable way to pick the right one for a given requirement is to test each against it directly.

This is the kind of hands-on CML work Veloce does on Revenue Cloud (ARM) implementations – choosing the rule that fits the business need, not just the one that works.

FAQ: setDefault vs require in CML

Why does my pre-selected product keep coming back after I remove it?

You are almost certainly using preference or require. Both re-enforce the selection whenever their condition holds, so an unselection does not survive. To let the user opt out, use setDefault, which recommends once when its condition changes and then stays passive.

Why doesn't preference let me unselect the product?

Because in this scenario preference behaves like require – it keeps satisfying the expression while the condition is true. It is the right tool when you want the selection enforced, and the wrong one when you want it optional.

Can setDefault target a product subtype rather than a specific product?

Yes. Unlike the default function, which needs a final type with products associated to it, setDefault can target an abstraction such as BiographyBook

What does the third argument of setDefault do?

It is a message displayed when the expression is not satisfied, reminding the user that the product is recommended though not enforced. Note that it also appears when more biography books are selected than the expression allows, so it is a hint rather than a strict validation.

When should I use require instead?

When the selection must be enforced. If the business rule is “while this condition is true, this product has to be in the bundle,” require is correct. If the rule is “suggest this product, but the user decides,” use setDefault.

Where can I read the official reference?

The setDefault rule page in the Revenue Cloud Developer Guide is the starting point. This article goes beyond it to show where the rule – and its alternatives – behave in ways the reference does not spell out.

Latest Blog Posts
Ready to accelerate your revenue growth?