Vaadin Component Basics
All Vaadin components consist of a client-side HTML element and a server-side Java component. The server-side components extend the com.vaadin.flow.component.Component base class. Vaadin provides support for keeping the client-side element and server-side component in sync.
Defining a Root Element
Every component is associated with a root Element, which is accessible from the Component.getElement() method. The @Tag annotation specifies the HTML tag name of the root element.
You use the Element API to interact with the client-side elements, for example by manipulating attributes, properties and styles, and reacting to events. See the Element API reference guide for more information.
TextField component based on an <input> elementSource code
TextField.java
You can use predefined constants in the @Tag annotation. For example, the @Tag("input") annotation is equivalent to @Tag(Tag.INPUT). Most tag names have a constant, but not all.
Using Multiple Elements
A component can consist of multiple elements in the DOM tree. In this case, the root element is still defined using the @Tag annotation, and other elements are created and appended to the root element using the Element API.
LabeledTextField component with <input> and <label> elements inside a <div> root elementSource code
LabeledTextField.java
package com.vaadin.demo.reference.componentinternals.basics;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.dom.Element;
@Tag("div")
public class LabeledTextField extends Component {
private final Element labelElement = new Element("label");
private final Element inputElement = new Element("input");
public LabeledTextField() {
getElement().appendChild(labelElement, inputElement);
}
}LabeledTextField.java
This generates the following DOM-tree structure in the browser:
Source code
HTML
<div>
<label></label>
<input>
</div>Extending a Component
If you extend an existing component, the root element is inherited from the parent component class. You can still use the Element API to manipulate the root element or create and append child elements. However, you don’t need to use the @Tag annotation in this case.
LabeledTextField component created aboveSource code
CustomTextField.java
package com.vaadin.demo.reference.componentinternals.basics;
import com.vaadin.flow.dom.Element;
public class CustomTextField extends LabeledTextField {
private final Element errorElement = new Element("span");
public CustomTextField() {
super();
getElement().appendChild(errorElement);
}
}CustomTextField.java
This generates the following DOM-tree structure in the browser:
Source code
HTML
<div>
<label></label>
<input>
<span></span>
</div>Composite Components
When you create a new component by combining existing components, you should extend the Composite<T> class, where T is the type of the root component. This does not affect the DOM structure, but prevents users of your component from accessing the API of the root component directly.
CompositeTextField component with Input and NativeLabel components inside a Div root componentSource code
CompositeTextField.java
package com.vaadin.demo.reference.componentinternals.basics;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Input;
import com.vaadin.flow.component.html.NativeLabel;
public class CompositeTextField extends Composite<Div> {
private final NativeLabel label;
private final Input input;
public CompositeTextField(String labelText, String value) {
label = new NativeLabel();
label.setText(labelText);
input = new Input();
input.setValue(value);
// Users of CompositeTextField can't access the div directly since
// getContent() is protected.
var div = getContent();
div.add(label, input);
}
// Declare public API here.
}CompositeTextField.java
This generates the following DOM-tree structure in the browser:
Source code
HTML
<div>
<label></label>
<input>
</div>Creating a Java API
The Element API provides low-level access to the client-side element. You typically want to create a higher-level Java API that hides these details from the users of your component.
A Vaadin component API consists of:
-
Properties for accessing and setting values. This is covered in the Component Properties reference guide.
-
Events and event listeners for reacting to user actions. This is covered in the Declaring and Firing Component Events reference guide.
-
Other methods that act on the component. Some of them may use the Element API to call JavaScript functions on the client side. This is covered in the Remote Procedure Calls reference guide.
value property, and reacting to value changesSource code
TextField.java
Lifecycle Callbacks
The topmost component in any component hierarchy is the UI instance. The UI may represent an entire browser window (or tab), or some part of an HTML page where a Vaadin application is embedded.
Attach to UI
Whenever a component is added to a UI, either directly or indirectly through another component, it is considered to be attached to the UI. When a component is removed from the UI, it is considered to be detached from the UI.
The Component class provides an onAttach() method that is called when the component has been attached to a UI. Override this method to perform initialization that requires the component to be attached. The default implementation is empty, so you don’t need to call super.onAttach() unless you are extending another component class that requires it.
onAttach() methodSource code
UserNameLabel.java
package com.vaadin.demo.reference.componentinternals.basics;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
@Tag("div")
public class UserNameLabel extends Component {
@Override
protected void onAttach(AttachEvent attachEvent) {
// This assumes the username has been stored in the session after login
String userName = (String) attachEvent.getSession()
.getAttribute("username");
getElement().setText("Hello %s, weclome back!".formatted(userName));
}
}UserNameLabel.java
You can also use the Component.addAttachListener() method to register an attach listener instead of overriding the onAttach() method. The listener is invoked after the onAttach() method. This is useful when you want to add attach behavior to an existing component without creating a subclass.
Detach from UI
The Component class also provides a onDetach() method that is called right before the component is detached from the UI. Override this method to perform cleanup, for example to release resources that were acquired in the onAttach() method. The default implementation is empty, so you don’t need to call super.onDetach() unless you are extending another component class that requires it.
onAttach() method and unsubscribing in the onDetach() methodSource code
ShoppingCartSummaryLabel.java
package com.vaadin.demo.reference.componentinternals.basics;
import java.util.EventObject;
import java.util.function.Consumer;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.DetachEvent;
import com.vaadin.flow.component.Tag;
@Tag("div")
public class ShoppingCartSummaryLabel extends Component {
private final Consumer<EventObject> eventHandler = this::onCartSummaryUpdate;
@Override
protected void onAttach(AttachEvent attachEvent) {
// This assumes the session already contains a ShopEventBus
var eventBus = attachEvent.getSession()
.getAttribute(ShopEventBus.class);
eventBus.register(eventHandler);
}
@Override
protected void onDetach(DetachEvent detachEvent) {
var eventBus = detachEvent.getSession()
.getAttribute(ShopEventBus.class);
eventBus.unregister(eventHandler);
}
private void onCartSummaryUpdate(EventObject event) {
// update cart summary ...
}
}
interface ShopEventBus {
void register(Consumer<EventObject> eventHandler);
void unregister(Consumer<EventObject> eventHandler);
}ShoppingCartSummaryLabel.java
You can also use the Component.addDetachListener() method to register a detach listener instead of overriding the onDetach() method. The listener is invoked after the onDetach() method. This is useful when you want to add detach behavior to an existing component without creating a subclass.
Attach-Scoped Setup and Cleanup
When the cleanup in onDetach() only undoes what onAttach() did, the two halves can be kept together with whenAttached(). The handler runs every time the component is attached — immediately, if the component is already attached — and the Registration it returns runs when the component is detached again.
whenAttached() and unsubscribing in the returned registrationSource code
CartSummaryWhenAttached.java
package com.vaadin.demo.reference.componentinternals.basics;
import java.util.EventObject;
import java.util.function.Consumer;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
@Tag("div")
public class CartSummaryWhenAttached extends Component {
public CartSummaryWhenAttached() {
whenAttached(ui -> {
// This assumes the session already contains a ShopEventBus
var eventBus = ui.getSession().getAttribute(ShopEventBus.class);
Consumer<EventObject> handler = this::onCartSummaryUpdate;
eventBus.register(handler);
return () -> eventBus.unregister(handler);
});
}
private void onCartSummaryUpdate(EventObject event) {
// update cart summary ...
}
}CartSummaryWhenAttached.java
Because the cleanup closes over the local variables of the handler, no field is needed to carry state from an attach to the matching detach. The handler receives the UI the component was attached to, and it can return null when there is nothing to clean up.
Removing the returned registration removes the handler and also runs any cleanup still pending from the latest attach. An exception thrown by the handler is propagated to the caller, whereas one thrown by the cleanup is passed to the session error handler, so that a failing cleanup does not prevent the rest of the detach handling from running.
The same method is available on the element level as Element.whenAttached(). Both are public, so they can also be used from the outside to hook into the life cycle of another component.
Setting Id
You can set an Id for any component. The Id is passed to the client side as the id of the corresponding element. However, an Id must be unique within the page, as it can be used to select the element in JavaScript code or CSS rules.
IdSource code
SetIdView.java
2E20FB94-B5FD-4105-B53B-3EECA329EFF3