> Markdown version of [UI State](https://vaadin.com/docs/next/flow/ui-state). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Manage UI State with Signals

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.

- [Component Bindings](https://vaadin.com/docs/next/flow/ui-state/building-ui.md): Using signals to build reactive user interfaces with component bindings.
- [Local Signals](https://vaadin.com/docs/next/flow/ui-state/local-signals.md): Using local signals for UI-only state management.
- [Effects and Computed Signals](https://vaadin.com/docs/next/flow/ui-state/effects-computed.md): Using effects and computed signals for reactive UI updates.
- [Shared Signals](https://vaadin.com/docs/next/flow/ui-state/shared-signals.md): Using shared signals for thread-safe, multi-user state management.
- [Element Bindings](https://vaadin.com/docs/next/flow/ui-state/element-bindings.md): Binding signals to Element properties, attributes, and styles for advanced use cases.
- [Transactions](https://vaadin.com/docs/next/flow/ui-state/transactions.md): Using transactions for atomic signal operations.
- [Usage Examples](https://vaadin.com/docs/next/flow/ui-state/usage-examples.md): Practical guides for solving common state management challenges with Signals

## <a id="why-signals"></a>Why Signals?

Consider a simple counter-component consisting of a counter value and a button that increments the counter and updates the text of the button each time it is clicked. Without signals, you need to manually update the button text value whenever the counter changes:

```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 button text updates automatically. In this version, each time the button is clicked, the `Integer` value in the `count` `ValueSignal` is updated by adding 1 to it, and because the signal is bound to the text of the button, it tells the button to update itself with the new value:

```java
import com.vaadin.flow.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:

```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.set(e.getValue()));

        TextField lastNameField = new TextField("Last name");
        lastNameField.addValueChangeListener(e -> lastName.set(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.get() + " " + lastName.get()));

        saveButton.bindEnabled(Signal.computed(() ->
            !firstName.get().isEmpty() && !lastName.get().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.

## <a id="key-features"></a>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.

## <a id="core-concepts"></a>Core Concepts

### <a id="signals"></a>Signals

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

```java
ValueSignal<String> name = new ValueSignal<>("initial value");
name.set("new value"); // Set the value
String current = name.peek(); // Get the value outside reactive contexts
```

### <a id="effects"></a>Effects

Effects are the callbacks that run reactively to a state change and that update the UI accordingly. When you bind a signal to a component, an effect tracks which signals are read and re-runs when they change. In the example below, the effect is a lambda function passed into `Signal.computed` that updates the text of the `span` whenever `firstName` or `lastName` changes:

```java
// This binding creates an effect that updates text when signals change
span.bindText(Signal.computed(() ->
    firstName.get() + " " + lastName.get()));
```

For custom logic, you can create effects directly. In this example, the effect is a lambda callback passed into `Signal.effect` that prints a message to the console whenever `firstName` or `lastName` changes:

```java
Signal.effect(component, () -> {
    // This runs whenever firstName or lastName changes
    System.out.println("Name changed to: " + firstName.get() + " " + lastName.get());
});
```

### <a id="computed-signals"></a>Computed Signals

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

```java
Signal<String> fullName = Signal.computed(() ->
    firstName.get() + " " + lastName.get());
```

### <a id="transactions"></a>Transactions

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

```java
Signal.runInTransaction(() -> {
    firstName.set("John");
    lastName.set("Doe");
    // Both update atomically - observers never see partial state
});
```

## <a id="local-vs-shared-signals"></a>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 Classes | `ValueSignal`, `ListSignal` | `SharedValueSignal`, `SharedNumberSignal`, `SharedListSignal`, `SharedMapSignal`, `SharedNodeSignal` |

### <a id="choosing-between-local-and-shared-signals"></a>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

- You need dynamic lists for a single user (use `ListSignal` for add/remove operations)

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](https://vaadin.com/docs/next/flow/advanced/server-push.md#push.configuration.enabling) so changes are pushed to all users immediately.
