The Element API and Web Components
|
This section documents the current Vaadin release line — Vaadin 24 LTS / 25.x, Java 17+, Spring Boot 3 / Jakarta EE 10 — as published at the official Vaadin documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Flow (server-side Java) is the authoring style used throughout, with Hilla / React shown where it differs; Vaadin 7 and the pre-Flow architecture appear only as migration contrast. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vaadin ships major releases roughly twice a year and its ecosystem iterates. This section’s bibliography lists the reference material consulted while preparing these pages. |
Flow’s component classes are wrappers over a server-side representation of DOM elements. When no component
exists for what you need — a raw element, a browser API call, or a third-party Web Component — the Element
API gives you direct, type-checked access to that DOM from Java. This page follows
the Element API guide and
Integrating Web Components. It replaces the
Vaadin 7 GWT connector / shared-state / RPC widget model.
Working with elements
Every Component exposes getElement(); you can also build elements directly:
Element wrapper = new Element("section");
wrapper.setAttribute("class", "panel");
wrapper.getStyle().set("padding", "var(--lumo-space-m)");
wrapper.setText("Hello");
Element input = new Element("input");
input.setProperty("placeholder", "Search…"); // a JS property, not an attribute
input.setAttribute("type", "search");
wrapper.appendChild(input);
getElement().appendChild(wrapper);
Attributes are strings in HTML; properties are live values on the DOM object. setProperty accepts
String, boolean, double and JsonValue. getStyle() sets inline CSS.
DOM events
addEventListener wires a server-side listener to a client event; @EventData pulls fields off the event or
element into the callback, and setFilter / debounce cut traffic:
input.addEventListener("input", e -> {
String value = e.getEventData().getString("element.value");
filter(value);
}).addEventData("element.value").debounce(300);
On a component, @DomEvent maps a custom element event to a typed ComponentEvent:
@DomEvent("selection-change")
public static class SelectionChangeEvent extends ComponentEvent<MyPicker> {
private final String value;
public SelectionChangeEvent(MyPicker source, boolean fromClient,
@EventData("event.detail.value") String value) {
super(source, fromClient);
this.value = value;
}
}
Calling JavaScript
executeJs(expression, params…) runs a snippet in the element’s context; $0, $1, … are the parameters,
and the returned PendingJavaScriptResult resolves with the expression’s value:
getElement().executeJs("return navigator.language").then(String.class, lang ->
applyLocale(lang));
getElement().executeJs("$0.scrollIntoView({behavior:'smooth'})", target.getElement());
@ClientCallable exposes a server method to client code by name:
@ClientCallable
public void onClientReady(String token) {
session.setToken(token);
}
// on the client: this.$server.onClientReady(myToken)
See Client-server RPC.
Integrating a Web Component
To use a published custom element, declare its npm package and module and map its tag to a Java class.
@NpmPackage adds the dependency; @JsModule imports the module; @Tag binds the element:
@Tag("emoji-picker")
@NpmPackage(value = "emoji-picker-element", version = "1.21.0")
@JsModule("emoji-picker-element/index.js")
public class EmojiPicker extends Component {
private static final PropertyDescriptor<String, String> LOCALE =
PropertyDescriptors.propertyWithDefault("locale", "en");
public void setLocale(String locale) { LOCALE.set(this, locale); }
public String getLocale() { return LOCALE.get(this); }
public EmojiPicker() {
getElement().addEventListener("emoji-click", e -> fireEvent(
new EmojiSelectedEvent(this, true,
e.getEventData().getString("event.detail.unicode"))));
}
}
PropertyDescriptor gives a typed getter/setter over a DOM property with a default. Reusable behaviour — HasSize, HasStyle, Focusable<T>, or your own — is added through mixin interfaces whose default methods
call the Element API, so a component gains setWidth, addClassName, focus() and the like just by
implementing them. @CssImport and @JavaScript load a stylesheet or a plain script into the page.
getElement().getShadowRoot() (or attachShadow()) reaches a shadow tree when you build one server-side.
See also
-
Building Custom Components — composing and packaging components, and custom fields.
-
Hilla and React Views — wrapping a React component instead.
-
Theming and Styling —
@CssImportand styling shadow DOM with::part(). -
JavaScript Development and TypeScript Reference — the client-side languages.
-
Element API and Integrating Web Components — the references.