Skip to content

[DRAFT] Signal binding to Vaadin Components #8366

@mshabarov

Description

@mshabarov

Description

Integrate Signals API into Vaadin Components through the base Component class, mix-in HasXyz interfaces or by adding direct methods to Flow Components to provide a way of building reactive UI with Java.

Tier

Free

License

Apache 2.0

Motivation

Background

Vaadin 24.8, 24.9 have shipped the base Signals class hierarchy and helpers in ComponentEffect.
Vaadin 25.0 has added Element-level binding methods and helpers in ElementEffect.
Vaadin 25.1 is now aims to fully integrate Signal API into Vaadin Flow Components, so that developers can us this way of building UI as a primary.

Problem

Overall problem: It is common in Vaadin to explicitly handle UI state change by users in the event listeners, maintain clean ups and set the UI state explicitly through the component's methods. This may lead to a boiler-plate code that makes the view classes be unreadable and hard-maintained. Signals on the other hand may simplify the UI development by implicitly handling the state change no matter what kind of source did this - single user event, multi-user events or asynchronous background event, and implicitly handling the component UI attached/detached state.

Signal API current status: Whereas the Signal and related classes are available, it's not yet possible to code the reactive UIs using signals and without using existing low-level Element API, explicit effect functions and defining addValueChangeListener(), nor there is a way of binding signals to a form.

Solution

Signals integration to Vaadin Components would include:

  • Signal binding methods to Component class and mix-in interfaces for commonly used component properties (visible, enabled, value)
  • Component-specific signal binding methods (e.g. setHelperText)
  • Two-way signal binding for standalone field components and forms.
  • Ensure Signal API is serializable.

This in the end means the component provide bind methods for the component features for the following kind of bindings

  • one way binding (e.g. text)
  • two-way binding (e.g. value)
  • callbacks that use signal as a state (e.g. item label generator),
    and the Binder API is adjusted/extended so it's features can use Signal and benefit from two-way bindings.

As an example of how the code may look like when using signals, guess the products price calculator add VAT to the base price and updates all product cards:

Example with the value change listeners:

NumberField vatField = new NumberField("VAT %");
// 1. Value change listener explicitly added
Registration vatChangeRegistration = vatField.addValueChangeListener(event -> {
    productCards.forEach(productCard -> {
           String newPriceLine = getUpdatedPrice(event.getValue());
           // 2. Product component explicitly updated with the new price
           productCard.setPriceLine(newPriceLine);
    });
});

// 3. Duplicated code that repeats the listener code for component initialization
productCards.forEach(productCard -> {
      // initialize the UI nearly the same way
});

// 4. Manual cleanup on detaching
vatChangeRegistration.remove();

Same example with signals:

// 3. State initialized once a signal is instantiated
ReferenceSignal<Double> vatSignal = new ReferenceSignal<>(24.0);

// 1. Two-way binding, no explicit listeners
// 4. No registration, signal is deactivated on detach
vatField.bindValue(vatSignal);

// Computed signal
Signal<String> priceWithVatText = vatSignal.map(this::getUpdatedPrice);

// 2. Bind computed signal to the product 
productCards.forEach(productCard -> productCard.bindText(priceWithVatText));

Requirements

  • Component and Element classes shall implement void bindVisible(Signal<Boolean> visibleSignal) new methods. Component delegates to Element and has the same contract;

  • HasEnabled mix-in and Element class shall implement void bindEnabled(Signal<Boolean> enabledSignal) new methods. HasEnabled delegates to Element and has the same contract;

  • HasText mix-in interface shall implement void bindText(Signal<String> textSignal) new method and delegate to the same existing method in Element;

  • HasValue<T> mix-in and Element shall implement void bindValue(WritableSignal<T> valueSignal) new methods. Note that this is a two-way binding that also updates the signal value when a ValueChangeEvent would be fired.

  • More requirements...

  • Documentation shall recommend using signals in all places where handling components/UI state is applicable.

  • Feature flag shall be removed.

Nice-to-haves, but may not fit into 25.1 schedule

  • Helpers for debugging Signals
  • Binding to items in HasItems components
  • Router integration
  • Clustering

Risks, limitations and breaking changes

Risks

Limitations

Breaking changes

Out of scope

No response

Materials

No response

Metrics

No response

Pre-implementation checklist

  • Estimated (estimate entered into Estimate custom field)
  • Product Manager sign-off
  • Engineering Manager sign-off

Pre-release checklist

  • Documented (link to documentation provided in sub-issue or comment)
  • UX/DX tests conducted and blockers addressed
  • Approved for release by Product Manager

Security review

None

Metadata

Metadata

Assignees

No one assigned

    Labels

    FlowPRDdraftThe acceptance criteria that is still WIP

    Type

    No type

    Projects

    Status

    March 2026 (25.1)

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions