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

# Element Bindings

This section covers low-level `Element` bindings for direct DOM manipulation. These APIs are useful when building custom components or when you need fine-grained control over element attributes and properties.

> **Note:** For most applications, use the component-level binding methods covered in [Component Bindings](https://vaadin.com/docs/next/flow/ui-state/building-ui.md). Component methods like `bindText()`, `bindVisible()`, and `bindEnabled()` provide a simpler API for common use cases. Use Element bindings when:
>
> - Building custom components
>
> - Working with HTML attributes not exposed by component APIs
>
> - Binding element properties for JavaScript interoperability

## <a id="binding-rules"></a>Binding Rules

All element bindings follow consistent rules:

- When a feature is bound to a signal, its value is kept synchronized with the signal value while the element is in the attached state

- When the element is detached, signal value changes have no effect

- Passing `null` as the signal unbinds the existing binding

- While a signal is bound, any attempt to set the value manually (other than through the signal) throws `BindingActiveException`

- Attempting to bind a new signal while one is already bound throws `BindingActiveException`

## <a id="text-binding"></a>Text Binding

`Element#bindText(Signal<String> signal)`

```java
// SharedNumberSignal's Double type must be mapped to String
Signal<String> signal = counter.map(value -> String.format("Clicked %.0f times", value));

span.getElement().bindText(signal);
// span's text content is now "Clicked 0 times"
```

Binding lifecycle

```java
span.getElement().getText(); // returns "Clicked 0 times"
span.getElement().setText(""); // throws BindingActiveException

span.getElement().removeFromParent(); // detaching from the UI
span.getElement().getText(); // returns "Clicked 0 times"
span.getElement().setText(""); // throws BindingActiveException
counter.set(5); // updating the signal value
span.getElement().getText(); // returns "Clicked 0 times"
add(span); // re-attaching the element to the UI
span.getElement().getText(); // returns "Clicked 5 times"

span.getElement().bindText(null); // unbinds the existing binding
span.getElement().getText(); // returns "Clicked 5 times"
span.getElement().setText("");
span.getElement().getText(); // returns ""
```

## <a id="attribute-binding"></a>Attribute Binding

`Element#bindAttribute(String attribute, Signal<String> signal)`

```java
ValueSignal<String> label = new ValueSignal<>("Close dialog");

button.getElement().bindAttribute("aria-label", label);
// DOM has "<button aria-label="Close dialog">"

label.set(null);
// DOM has "<button>" (attribute removed)
```

## <a id="property-binding"></a>Property Binding

Property binding supports various value types: `String`, `Boolean`, `Double`, `BaseJsonNode`, `Object` (bean), `List` and `Map`.

> **Note:** Typed Lists and Maps are not supported. The signal must be of type `Signal<List<?>>` or `Signal<Map<?,?>>`.

`Element#bindProperty(String name, Signal<T> signal, SerializableConsumer<T> writeCallback)`

The third parameter is a write callback that propagates client-side property changes back to the signal. Pass `null` for read-only bindings where changes only flow from signal to element.

A value that arrives from the client isn’t guaranteed to have the type of the bound signal — the client is free to set a property to anything. A value the signal can’t hold is rejected: a warning is logged, and the property is reverted on the client to the value the signal holds. The write callback isn’t expected to handle this itself, so a binding needs no defensive code for it. (since V25.3)

```java
ValueSignal<Boolean> hidden = new ValueSignal<>(false);

span.getElement().bindProperty("hidden", hidden, null);
hidden.set(!hidden.peek()); // toggles 'hidden' property
```

String type

```java
ValueSignal<String> title = new ValueSignal<>("Hello");
span.getElement().bindProperty("title", title, null);
title.set("World"); // updates 'title' property
```

Double type

```java
SharedNumberSignal width = new SharedNumberSignal();
width.set(100.5);
span.getElement().bindProperty("width", width, null);
width.incrementBy(50); // updates 'width' property to 150.5
```

Object (bean) type

```java
record Person(String name, int age) {}

ValueSignal<Person> person = new ValueSignal<>(new Person("John", 30));
span.getElement().bindProperty("person", person, null);
person.set(new Person("Jane", 25));
// element.person is now {name: 'Jane', age: 25}
```

List type

```java
ValueSignal<List<String>> items = new ValueSignal<>(List.of("Item 1", "Item 2"));
span.getElement().bindProperty("items", items, null);
items.set(List.of("Item A", "Item B", "Item C"));
// element.items is now ['Item A', 'Item B', 'Item C']
```

Map type

```java
ValueSignal<Map<String, String>> config = new ValueSignal<>(Map.of("key1", "value1"));
span.getElement().bindProperty("config", config, null);
config.set(Map.of("key1", "value1", "key2", "value2"));
// element.config is now {key1: 'value1', key2: 'value2'}
```

Two-way binding with property change listener

```java
ValueSignal<Boolean> hidden = new ValueSignal<>(false);

// Bind the 'hidden' property to the signal with a write callback for two-way sync
span.getElement().bindProperty("hidden", hidden, hidden::set);

// Add a property change listener that synchronizes on 'change' DOM event
span.getElement().addPropertyChangeListener("hidden", "change", event -> {
    // When the property changes on the client (via DOM event),
    // the changed value is propagated back to the signal via the write callback
    Notification.show("'hidden' property changed to: " + event.getValue());
});

// After a property change event from the client, signal.get() returns the updated value
```

## <a id="flashing-a-css-class"></a>Flashing a CSS Class

`Element#flashClass(String className)`

Use `flashClass()` to temporarily add a CSS class that triggers a CSS animation, then automatically removes it when the animation ends. This is useful for visual feedback like highlighting a value that changed:

```java
element.flashClass("highlight");
```

The method removes the class (if present), forces a DOM reflow, and re-adds it to restart the animation. An `animationend` listener removes the class when the animation completes. If no CSS animation is defined for the class, it is removed immediately.

Define the animation in CSS:

```css
.highlight {
    animation: flash 0.5s ease-out;
}
@keyframes flash {
    from { background-color: yellow; }
    to { background-color: transparent; }
}
```

You can combine `flashClass()` with signal effects to flash whenever a value changes:

```java
SharedNumberSignal counter = new SharedNumberSignal();

Span counterSpan = new Span();
counterSpan.bindText(counter.map(c -> String.format("Count: %.0f", c)));

Signal.effect(counterSpan, () -> {
    counter.get(); // Track the counter signal
    counterSpan.getElement().flashClass("highlight");
});
```

## <a id="classlist-binding"></a>ClassList Binding

`ClassList#bind(String name, Signal<Boolean> signal)`

```java
ValueSignal<Boolean> foo = new ValueSignal<>(false);
ValueSignal<Boolean> bar = new ValueSignal<>(true);

span.getElement().getClassList().bind("foo", foo);
span.getElement().getClassList().bind("bar", bar);
// DOM has "<span class='bar'>"

foo.set(true);
// DOM has "<span class='bar foo'>"

span.getElement().getClassList().clear();
// DOM has "<span class>". Binding is also removed.
```

### <a id="group-binding"></a>Group Binding

`ClassList#bind(Signal<List<String>> signal)`

Use the group binding variant to bind an entire list of class names to a signal. When the signal value changes, all previous class names from the binding are removed and the new ones are added:

```java
ValueSignal<List<String>> classes = new ValueSignal<>(List.of("card", "elevated"));

span.getElement().getClassList().bind(classes);
// DOM has "<span class='card elevated'>"

classes.set(List.of("card", "flat"));
// DOM has "<span class='card flat'>"

classes.set(List.of());
// DOM has "<span class=''>"
```

## <a id="style-binding"></a>Style Binding

`Style#bind(String name, Signal<String> signal)`

```java
ValueSignal<String> color = new ValueSignal<>("black");
ValueSignal<String> background = new ValueSignal<>("white");

span.getElement().getStyle().bind("color", color);
span.getElement().getStyle().bind("background", background);
// DOM has "<span style='color: black; background: white'>"

color.set("red");
background.set("gray");
// DOM has "<span style='color: red; background: gray'>"

background.set(""); // same with null
// DOM has "<span style='color: red;'>"

span.getElement().getStyle().clear();
// DOM has "<span style>". Binding is also removed.
```

## <a id="themelist-binding"></a>ThemeList Binding

> **Note:** Theme names are per-component style variants. They are not how an application switches between light and dark: use the `ColorScheme` annotation or `Page.setColorScheme()` for that, as described in [Light & Dark Color Schemes](https://vaadin.com/docs/next/styling/themes.md#color-schemes).

`ThemeList#bind(String name, Signal<Boolean> signal)`

```java
ValueSignal<Boolean> smallVariant = new ValueSignal<>(false);

component.getThemeList().bind("small", smallVariant);
// Theme "small" is applied when smallVariant is true

smallVariant.set(true);
// Component now has "small" theme applied
```

### <a id="group-binding-2"></a>Group Binding

`ThemeList#bind(Signal<List<String>> signal)`

Use the group binding variant to bind an entire list of theme names to a signal:

```java
ValueSignal<List<String>> themes = new ValueSignal<>(List.of("compact", "row-stripes"));

component.getThemeList().bind(themes);
// Component has themes "compact" and "row-stripes"

themes.set(List.of("no-border"));
// Component now has only "no-border" theme
```

For component-level theme binding examples, see [Binding Theme Names](https://vaadin.com/docs/next/flow/ui-state/building-ui.md#binding-theme-names).

## <a id="visibility-binding"></a>Visibility Binding

Components provide `bindVisible()` directly. The Element-level binding follows the same pattern:

`Element#bindVisible(Signal<Boolean> signal)`

```java
ValueSignal<Boolean> visible = new ValueSignal<>(true);

element.bindVisible(visible);
// Element is visible when visible is true

visible.set(false);
// Element is now hidden
```

For component-level examples, see [Binding Visibility](https://vaadin.com/docs/next/flow/ui-state/building-ui.md#binding-visibility).

## <a id="enabled-binding"></a>Enabled Binding

Components provide `bindEnabled()` directly. The Element-level binding:

`Element#bindEnabled(Signal<Boolean> signal)`

```java
ValueSignal<Boolean> enabled = new ValueSignal<>(true);

button.getElement().bindEnabled(enabled);
// Button is enabled when enabled is true

enabled.set(false);
// Button is now disabled
```

For component-level examples, see [Binding Enabled State](https://vaadin.com/docs/next/flow/ui-state/building-ui.md#binding-enabled-state).

## <a id="change-callbacks"></a>Change Callbacks

All binding methods return a `SignalBinding` that supports change callbacks via `onChange()`:

```java
span.bindText(priceSignal.map(p -> "$" + p))
    .onChange(ctx -> {
        if (ctx.isBackgroundChange()) {
            ctx.getElement().flashClass("highlight");
        }
    });
```

The `BindingContext` provides:

- `getOldValue()` / `getNewValue()` — the previous and current bound values

- `getElement()` — the target element

- `getComponent()` — the owning component (if any)

- `isInitialRun()` — whether this is the first execution

- `isBackgroundChange()` — whether triggered by another session or background thread

## <a id="html-content-binding"></a>HTML Content Binding

`Html#bindHtmlContent(Signal<String> signal)`

```java
ValueSignal<String> htmlContent = new ValueSignal<>("<strong>Bold text</strong>");

Html html = new Html("<span></span>");
html.bindHtmlContent(htmlContent);
// HTML content is now "<strong>Bold text</strong>"

htmlContent.set("<em>Italic text</em>");
// HTML content is now "<em>Italic text</em>"
```

> **Warning:** Be careful with HTML content binding to avoid XSS vulnerabilities. Never bind user-provided content directly without proper sanitization.

## <a id="form-field-bindings"></a>Form Field Bindings

Form field bindings provide two-way synchronization between fields and signals. For comprehensive examples including all supported field types, see [Two-Way Form Field Binding](https://vaadin.com/docs/next/flow/ui-state/building-ui.md#two-way-form-field-binding) in Component Bindings.

### <a id="two-way-value-binding"></a>Two-Way Value Binding

`HasValue#bindValue(Signal<V> signal, SerializableConsumer<V> setter)`

The `bindValue()` method creates a two-way binding between a form field and a signal:

```java
ValueSignal<String> nameSignal = new ValueSignal<>("");

TextField nameField = new TextField("Name");
nameField.bindValue(nameSignal, nameSignal::set);

// User types in field -> signal is updated via setter
// Signal changes -> field is updated
```

### <a id="read-only-binding"></a>Read-Only Binding

`HasValue#bindReadOnly(Signal<Boolean> signal)`

Binds the read-only state of a form field to a boolean signal:

```java
ValueSignal<Boolean> readOnly = new ValueSignal<>(false);

TextField field = new TextField();
field.bindReadOnly(readOnly);

readOnly.set(true);
// Field is now read-only
```

### <a id="required-indicator-binding"></a>Required Indicator Binding

`HasValue#bindRequiredIndicatorVisible(Signal<Boolean> signal)`

Binds the required indicator visibility of a form field to a boolean signal:

```java
ValueSignal<Boolean> required = new ValueSignal<>(true);

TextField field = new TextField("Name");
field.bindRequiredIndicatorVisible(required);
```

## <a id="signalpropertysupport-helper"></a>SignalPropertySupport Helper

Not all component features delegate directly to the state in `Element`. For those features, the `SignalPropertySupport` helper class ensures that state management behaves consistently with other element bindings.

```java
class MyComponent extends Div {
    private final SignalPropertySupport<String> textProperty =
            SignalPropertySupport.create(this, value -> {
                getElement().executeJs("this.textContent = 'Content: ' + $0", value);
            });

    public String getTextContent() {
        return textProperty.get();
    }

    public void setTextContent(String text) {
        textProperty.set(text);
    }

    public void bindTextContent(Signal<String> textSignal) {
        textProperty.bind(textSignal);
    }
}
```

Usage:

```java
MyComponent component = new MyComponent();
component.bindTextContent(counter.map(v -> "Signal value: " + v));
add(component);
// textContent in browser is "Content: Signal value: 0.0"

component.getTextContent(); // returns "Signal value: 0.0"
component.setTextContent(""); // throws BindingActiveException

component.bindTextContent(null); // unbinds the existing binding
component.setTextContent("");
component.getTextContent(); // returns ""
```
