Ordering and priority
When multiple states match at the same time,
StateEngine
must decide
which one becomes the winner. This decision is deterministic and based
on priority, specificity and rule order.
When a winner needs to be chosen, the resolution process is simple:
- Expand nested rules into flat states.
-
Evaluate all
whenconditions. - Collect all matching states.
- Sort them by priority.
- Break ties by rule order.
- Select the winner.
Only one state becomes the winner (unless you use
eligible
or
overlay,
which modify how additional states run) or unless you specify
{ active_mode="winner" }
as your state engine option.
Default priority
If you never specified a priority, every state would have the same default priority. In that case, rule order determines the winner — states defined later win over earlier ones. For example:
static states = [
[ { status: ["paid", "available"] }, (c) => c.enableShipping() ],
[ { status: ["paid", "available"] }, (c) => c.lockOrder() ],
];
When both conditions inevitably match, the second rule wins because it appears later, but the first is never the winner here. This is an unrealistic example, but it demonstrates the point well.
Using priority metadata
You can override ordering with explicit priority:
static states = [
[ { status: ["paid", "available"] }, (c) => c.enableShipping(),
{ priority: 10 }
],
[ { status: ["paid", "available"] }, (c) => c.lockOrder() ],
];
Now the first rule always wins, while the second one always loses and its transitions never run. If two states have different priorities the state with the higher numeric priority wins and rule order is only used when priorities are equal.
Specificity
In many cases, you don't need explicit
priority
because rule order
naturally encodes specificity. A more specific rule should usually appear
after a more general one:
static states = [
[ { status: "paid" }, (c) => c.enableShipping() ],
// More specific version of "paid"
[
{ status: "paid", total: (c, v) => v > 1000 },
(c) => c.enablePriorityHandling()
]
];
If
status = "paid"
and
total = 1500, both rules match.
Because the second rule is more specific and appears later,
it wins. This pattern keeps rules readable:
- General rules first.
- More specific refinements later.
Only reach for explicit
priority
when specificity cannot be expressed
cleanly through ordering.
Deterministic behavior
The resolution process is fully deterministic and follows these tenets:
- Same inputs always produce the same winner.
- No randomness is involved.
- Nested rules are expanded consistently.
- Equal priority states resolve by definition order.
This is important because state transitions often control business logic, not just UI and determinism ensures that behavior is predictable and debuggable.
Practical guidance
In most components, you don't need explicit priority. Simply ordering rules from least specific to most specific is usually enough.
Use
priority
only when:
- Two unrelated rules may match simultaneously.
- You want to make resolution explicit rather than relying on order.
- You are building more complex interaction models.
Clear ordering and occasional explicit priority are sufficient for almost all real-world components.