Clone on Gitea
Introduction
Explained by ducks
FAQ
Getting Started
Components
Component Basics
Auto Initialization From DOM
Templates
Children and Parents
Roles
Strictness
Fields
What is a Field
Binding Fields to DOM
Reading and Writing Fields
Value Casting
Flags
Events
Overview
Event Handlers
DOM Events
Custom Events
Propagation and Flow
Advanced Event Control
Beyond Components
States
Overview
StateEngine
DisplayStateEngine
Field Matchers
Ordering and priority
Debugging
Advanced usage
Ajax
ComponentUI
VirtualForm
Unit Testing
Helpers
Overview
Cookies
Arrays

Debugging states

When state rules grow, it becomes important to understand:

  • Which states are currently active?
  • Why did a particular state activate?
  • Why did another one not activate?
  • Which transitions actually ran?
  • Which targets were shown or hidden?

Both StateEngine and DisplayStateEngine provide built-in debugging, but they are enabled differently.

DisplayStateEngine debugging

DisplayStateEngine accepts configuration options as the first element of static display_states. This includes a debug option, which is an array specifying what to log. Here's how you enable debugging:

static display_states = [
    {
        visibility_mode: "whitelist",
        debug: [
            "active_states",
            "displayed_targets",
            "hidden_targets",
            "missing_targets",
            "timing"
        ]
    },

    [ { status: "draft" },     ["#draft"] ],
    [ { status: "paid" },      ["#paid"] ],
    [ { status: "cancelled" }, ["#cancelled"] ],
];

Now, whenever fields or flags change, the engine will print structured information to the console. Depending on what you enable, you may see:

  • Which states matched.
  • Which states became active or inactive.
  • Which targets are about to be shown.
  • Which targets are about to be hidden.
  • Warnings about missing targets.
  • Timing information for transitions.

Because debug is selective, you can keep output focused instead of noisy.

It also helps to provided a name in rule metadata:

[ { status: "paid" }, (c) => c.generateInvoice(), { name: "paid" } ]

That name appears in logs, making output easier to read. If you don't provide the name (which, admittedly, can make your state rules a bit noisy and appear longer than they need to be), debugger will print rule sequence number instead. However, a sequence number might be enough to identify the offending rule without relying on names. The only caveat with sequence number automatically assigned to rules is to remember how nested rules are processed.

StateEngine debugging

StateEngine supports debugging in the same way as DisplayStateEngine : you provide a debug option as the first element of the static states = [...] array. The debug option is an array specifying what to log:

static states = [
    {
        debug: [
            "active_states",
            "enter_states",
            "exit_states",
            "eligible_transitions",
            "applied_transitions",
            "skipped_transitions",
            "timing",
        ]
    },

    [ { status: "paid" }, (c) => c.generateInvoice(), { name: "paid" } ],
    [ { status: "cancelled" }, (c) => c.handleCancel(), { name: "cancelled" } ],
];

Now, whenever fields or flags change, StateEngine will print structured information to the console for the categories you enabled. Because debug is selective, you can keep output focused instead of noisy.

Nested rules in debug output

Nested rules appear in logs as independent states. This is because internally, nested rules are expanded into standalone states with inherited conditions and transitions. If a nested rule does not activate, debugging output helps identify which part of its combined condition failed.

As was mentioned earlier, sequence numbers are assigned to expanded rules, so if you have many of nested rules, it might be a good idea to assign names to them for easier debugging.

Missing targets

When DisplayStateEngine debugging includes "missing_targets", you will see warnings if a rule references a target that does not exist in the component template. For example:

["#shippng"] // typo

Instead of silently failing, the engine reports the issue clearly.

Recommended workflow

When something behaves unexpectedly:

  1. Enable debug with specific categories.
  2. Trigger the relevant field or flag change.
  3. Observe which states matched and which transitions ran.
  4. Adjust rules as needed.
  5. Disable debugging once the issue is resolved.

State rules are deterministic — bebugging simply reveals the engine's decision process so you can verify that your declarative logic matches your intent.

In the next section we will look at advanced usage topics such as overlay, eligible, and the difference between switch() and requestSwitch()