Docs

Documentation versions (currently viewingVaadin 25.1 (pre-release))

Manage UI State with Signals

Using signals in Flow to manage UI state reactively in Vaadin applications.

Signals enable reactive state management for Vaadin Flow applications. Instead of manually updating UI components when data changes, you declare the relationship between your data and UI once, and signals keep everything synchronized automatically.

Note
Preview Feature

This is a preview version of Signals. You need to enable it with the feature flag com.vaadin.experimental.flowFullstackSignals. Preview versions may lack some planned features, and breaking changes may be introduced in any Vaadin version. We encourage you to try it out and provide feedback to help us improve it.

Component Bindings
Using signals to build reactive user interfaces with component bindings.
Local Signals
Using local signals for UI-only state management.
Effects and Computed Signals
Using effects and computed signals for reactive UI updates.
Shared Signals
Using shared signals for thread-safe, multi-user state management.
Element Bindings
Binding signals to Element properties, attributes, and styles for advanced use cases.
Transactions
Using transactions for atomic signal operations.

Why Signals?

Consider a simple counter. Without signals, you need to manually update the UI whenever the value changes:

Source code
Java
public class TraditionalCounter extends VerticalLayout {
    private int count = 0;

    public TraditionalCounter() {
        Button button = new Button("Clicked 0 times");
        button.addClickListener(click -> {
            count++;
            button.setText("Clicked " + count + " times"); // Manual UI update
        });
        add(button);
    }
}

With signals, the UI updates automatically:

Source code
Java
import com.vaadin.signals.local.ValueSignal;

public class SignalCounter extends VerticalLayout {
    private final ValueSignal<Integer> count = new ValueSignal<>(0);

    public SignalCounter() {
        Button button = new Button();
        button.addClickListener(click -> count.update(c -> c + 1));
        add(button);

        // UI updates automatically when count changes
        button.bindText(count.map(c -> "Clicked " + c + " times"));
    }
}

The benefit becomes clear with more complex UIs. When multiple components depend on the same data, signals ensure they all stay in sync without manual coordination:

Source code
Java
public class UserProfile extends VerticalLayout {
    private final ValueSignal<String> firstName = new ValueSignal<>("");
    private final ValueSignal<String> lastName = new ValueSignal<>("");

    public UserProfile() {
        // Form fields
        TextField firstNameField = new TextField("First name");
        firstNameField.addValueChangeListener(e -> firstName.value(e.getValue()));

        TextField lastNameField = new TextField("Last name");
        lastNameField.addValueChangeListener(e -> lastName.value(e.getValue()));

        // Multiple UI elements that update automatically
        Span greeting = new Span();
        Span fullName = new Span();
        Button saveButton = new Button("Save");

        greeting.bindText(firstName.map(name -> "Hello, " + name + "!"));

        fullName.bindText(Signal.computed(() ->
            firstName.value() + " " + lastName.value()));

        saveButton.bindEnabled(Signal.computed(() ->
            !firstName.value().isEmpty() && !lastName.value().isEmpty()));

        add(firstNameField, lastNameField, greeting, fullName, saveButton);
    }
}

All three components (greeting, full name, save button) update automatically whenever the user types. No manual synchronization needed.

Key Features

Reactive

Changes to signal values automatically propagate to dependent parts of the UI.

Automatic Dependency Tracking

Effects automatically detect which signals they depend on.

Immutable Values

Signals work best with immutable values, such as strings or Java records, to ensure data consistency.

Transactions

Multiple operations can be grouped into a transaction that either succeeds or fails as a whole.

Thread-Safe

Signals can be updated from any thread without manual UI synchronization.

Core Concepts

Signals

A signal holds a value. When the value changes, all dependent parts of the UI update automatically without manual change listeners.

Source code
Java
ValueSignal<String> name = new ValueSignal<>("initial value");
name.value("new value"); // Set the value
String current = name.value(); // Get the value

Effects

Effects are the mechanism behind reactive updates. When you bind a signal to a component, an effect tracks which signals are read and re-runs when they change.

Source code
Java
// This binding creates an effect that updates text when signals change
span.bindText(Signal.computed(() ->
    firstName.value() + " " + lastName.value()));

For custom logic, you can create effects directly:

Source code
Java
ComponentEffect.effect(component, () -> {
    // This runs whenever firstName or lastName changes
    System.out.println("Name changed to: " + firstName.value() + " " + lastName.value());
});

Computed Signals

Computed signals derive their value from other signals and update automatically when dependencies change.

Source code
Java
Signal<String> fullName = Signal.computed(() ->
    firstName.value() + " " + lastName.value());

Transactions

Transactions group multiple signal operations into a single atomic unit. All operations succeed or fail together.

Source code
Java
Signal.runInTransaction(() -> {
    firstName.value("John");
    lastName.value("Doe");
    // Both update atomically - observers never see partial state
});

Local vs Shared Signals

Vaadin provides two categories of signals:

Local signals are for UI state within a single browser session - things like whether a panel is expanded, form validation state, or filter settings.

Shared signals are for state that needs synchronization across multiple users or requires transactional guarantees.

Local Signals Shared Signals

Scope

Single UI instance

Multiple users/sessions

Cluster Support

Single server only

Works across cluster

Transactions

No

Yes

Primary Class

ValueSignal

SharedValueSignal, SharedNumberSignal, etc.

Choosing Between Local and Shared Signals

Use local signals when:

  • The state is only relevant to a single user’s UI session

  • You need simple, fast state management without synchronization overhead

  • You’re managing UI state like form visibility, panel expansion, or local filters

Use shared signals when:

  • Multiple users need to see the same data in real-time

  • You need transactional guarantees for state changes

  • You’re building collaborative features like live dashboards or multi-user editing

Note
When using shared signals for multi-user scenarios, you need to enable push so changes are pushed to all users immediately.