> Markdown version of [Creating a Java API](https://vaadin.com/docs/v24/flow/create-ui/web-components/java-api-for-a-web-component). Section index: [llms.txt](https://vaadin.com/docs/v24/flow/llms.txt)

# Creating a Java API for a Web Component

While there are many ways to interact with a Web Component, there are a few typical ones worth noting. You could use properties on the element to define how it should behave. You can listen to events on the element to be notified when the user does something. Or you could call functions on the element to perform specific tasks, such as open a popup. And you can add sub-elements to define child content.

The component class included with the [Vaadin Add-on Starter](https://github.com/vaadin/addon-template), for example `MwcSlider.java`, is only a stub that handles the imports (see [Integrating a Web Component](https://vaadin.com/docs/v24/flow/create-ui/web-components.md) for more). This page demonstrates how to add functionality to the included stub class to transform it into a functioning Java API.

## <a id="setting-reading-properties"></a>Setting & Reading Properties

You can typically find the properties that an element supports in its JavaScript documentation on `npm` (see the [`<mwc-slider>`](https://github.com/material-components/material-web/tree/mwc/packages/slider) documentation). The `mwc-slider` has a boolean property named `discrete` that defines whether a numeric-value label displays when the slider thumb is pressed.

You can add getters and setters to match any property to create a corresponding Java setter-getter API for the property. An example of this would be adding a setter and getter for the `discrete` property in the `MwcSlider` class, like so:

```java
public void setDiscrete(boolean discrete) {
    getElement().setProperty("discrete", discrete);
}
public boolean isDiscrete() {
    return getElement().getProperty("discrete", false);
}
```

The setter here sets the given property to the requested value. The getter returns the property value, or `false` as the default value, if the property hasn’t been set. The default value should match the default of the Web Component property.

When you set `discrete` to `true` — which is done by updating the setter in the `AddonView` class — the pin appears when the slider thumb is pressed.

Calling `setDiscrete(true)` in the `AddonView` class, for example, would look like this:

```java
public AddonView() {
    MwcSlider slider = new MwcSlider();
    slider.setDiscrete(true);
    add(slider);
}
```

One disadvantage of writing the `getElement()` methods directly is that you have to repeat the property name in the getter and the setter. You can avoid this by using the `PropertyDescriptor` helper. This helper, and the factory methods in `PropertyDescriptors`, allows you to define the property as a single static field in the component and reference it from the getter and the setter.

For example, using the `PropertyDescriptor` helper and `propertyWithDefault()` factory method on the `discrete` property might be done like so:

```java
public class MwcSlider extends Component {

    private static final PropertyDescriptor<Boolean, Boolean> discreteProperty =
            PropertyDescriptors.propertyWithDefault("discrete", false);

    public void setDiscrete(boolean discrete) {
        discreteProperty.set(this, discrete);
    }
    public boolean isDiscrete() {
        return discreteProperty.get(this);
    }

}
```

The `discreteProperty` descriptor here defines a property with the name `discrete` and a default value of `false`, which matches the Web Component. Also, both a setter and getter of type `Boolean` are set through generics (`<Boolean, Boolean>`). The setter and getter code, though, only invokes the descriptor with the component instance.

## <a id="synchronizing-the-value"></a>Synchronizing the Value

The `mwc-slider` component allows the user to input a single value. To make it work automatically as a field, this kind of component should implement the `HasValue` interface. See [Binding Data to Forms](https://vaadin.com/docs/v24/flow/binding-data/components-binder.md) for more information on this.

The value needs to be synchronized automatically from the client to the server when the user changes it, as well as from the server to the client when it’s updated programmatically. Additionally, a value-change event should be fired on the server whenever the value changes.

Typically, when the `getValue()` method is based on a single-element property, the `AbstractSinglePropertyField` base class takes care of everything related to the value.

As an example, the following would extend the `AbstractSinglePropertyField` base class in the `MwcSlider` class:

```java
public class MwcSlider extends AbstractSinglePropertyField<MwcSlider, Integer> {

    public MwcSlider() {
        super("value", 0, false);
    }

}
```

The type parameters here define the component type (`MwcSlider`) returned by the `getSource()` method in value change events, and the value type (`Integer`). The constructor parameters in this example define the name of the element property that contains the value (`"value"`), the default value to use if the property isn’t set (`0`), and it determines whether `setValue(null)` should be allowed or throw an exception (i.e., `false` means that `null` isn’t allowed).

For more advanced cases that are still based on a one-element property, there is an alternative constructor that defines callbacks that convert between the low-level element property type and the high-level `getValue()` type. In cases where the value can’t be derived based on a single-element property, you can use the more generic `AbstractField` base class.

You can test this in the `AddonView` class. For example, testing the use of the alternative constructor in the `AddonView` class might look like this:

```java
public AddonView() {
    MwcSlider slider = new MwcSlider();
    slider.setDiscrete(true);
    slider.addValueChangeListener(e -> {
        String message = "The value is now " + e.getValue();
        if (e.isFromClient()) {
            message += " (set by the user)";
        }
        Notification.show(message, 3000, Notification.Position.MIDDLE);
    });
    add(slider);

    Button incrementButton = new Button("Increment using setValue", e -> {
        slider.setValue(slider.getValue() + 5);
    });
    add(incrementButton);
}
```

> **Note: Properties not related to the HasValue interface**
>
> For some Web Components, you need to update other properties that aren’t related to the `HasValue` interface. See [Creating A Simple Component Using the Element API](https://vaadin.com/docs/v24/flow/create-ui/creating-components/basic.md) for how to use the `@Synchronize` annotation to synchronize property values without automatically firing a value-change event.

## <a id="listening-to-events"></a>Listening to Events

All web elements fire a `click` event when the user clicks them. To allow the user of your component to listen to the `click` event, you can extend `ComponentEvent` and use the `@DomEvent` and `@EventData` annotations. For example, to extend `ComponentEvent` and use the `@DomEvent` and `@EventData` annotations in the `ClickEvent` class, you might do something like this:

```java
@DomEvent("click")
public static class ClickEvent extends ComponentEvent<MwcSlider> {

    private int x, y;

    public ClickEvent(MwcSlider source,
                      boolean fromClient,
                      @EventData("event.offsetX") int x,
                      @EventData("event.offsetY") int y) {
        super(source, fromClient);
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

}
```

`ClickEvent` here uses the `@DomEvent` annotation to define the name of the DOM event for which to listen: `click` in this case. Like all other events fired by a `Component`, it extends `ComponentEvent`, which provides a typed `getSource()` method. It uses two additional constructor parameters annotated with `@EventData` to get the click coordinates from the browser.

The expression inside each `@EventData` annotation is evaluated when the event is handled in the browser. It accesses DOM event properties using the `event.` prefix (e.g., `event.offsetX`) and element properties using the `element.` prefix.

> **Note: Constructor parameter requirements**
>
> The two first parameters of a `ComponentEvent` constructor must be `MwcSlider source, boolean fromClient`. These are filled automatically. All parameters following these two initial parameters must be annotated with `@EventData`.

You can now use the `ClickEvent` class as an argument when invoking the `addListener()` method on your `MwcSlider` component. The example here uses the `ClickEvent` class in the `addListener()` method:

```java
public Registration addClickListener(ComponentEventListener<ClickEvent> listener) {
    return addListener(ClickEvent.class, listener);
}
```

The `addListener()` method shown here in the superclass sets up everything related to the event, based on the annotations in the `ClickEvent` class.

You can test the integration in the `AddonView` class. To test the event integration in the `AddonView` class, you would do something like this:

```java
slider.addClickListener(e -> {
    Notification.show("Clicked at " + e.getX() + "," + e.getY(), 1000, Notification.Position.BOTTOM_START);
});
```

> **Tip: Use the Vaadin-provided ClickEvent for production**
>
> The click event was used here for illustrative purposes. In a real use case, you should use the `ClickEvent` provided by Vaadin, instead. This also provides additional event details.

> **Tip: Controlling propagation behavior**
>
> As the event data expression is evaluated as JavaScript, you can control propagation behavior using `@EventData("event.preventDefault()") String ignored`, for example. This is a work-around when there is no other API to control this behavior.

## <a id="calling-element-functions"></a>Calling Element Functions

In addition to properties and events, many elements offer methods that can be invoked for different reasons. For example, `vaadin-board` has a `refresh()` method that’s called whenever a change is made that the Web Component itself isn’t able to detect automatically. To call a function on an element, you can use the `callJsFunction()` method in `Element` as a way of providing an API.

Using the `callJsFunction()` method, for example, in the `MwcSlider` class to call the `layout` function on the `mwc-slider` element would be like this:

```java
public void layout(boolean skipUpdateUI) {
    getElement().callJsFunction("layout", skipUpdateUI);
}
```

You can test this in the `AddonView` class, for example, by using `layoutJSButton` in the `AddonView` class.

```java
Button layoutJSButton = new Button("Layout component using JS", e -> {
    slider.layout(false);
});
add(layoutJSButton);
```

The above method recomputes the dimensions and lays out again the component. This should be called if the dimensions of the slider itself, or any of its parent elements, change programmatically. To check the results manually, set width of `<mwc-slider>` to a fixed value and then click the button.

> **Tip: callJsFunction() parameters and return value**
>
> In addition to the method name, `callJsFunction()` accepts an arbitrary number of parameters of supported types. Current supported types are `String`, `Boolean`, `Integer`, `Double`, and the corresponding primitive types, `JsonValue`, and `Element` and `Component` references. It also returns a server-side promise for the JavaScript function’s return value. See the available methods in the Javadoc for more.

## <a id="mwc-slider-integration-result"></a>Mwc-Slider Integration Result

After you have completed the steps previously described, your `MwcSlider` class should be similar to the example below. In this example, Java API is provided by the `MwcSlider` class:

`MwcSlider.java`

```java
@Tag("mwc-slider")
@NpmPackage(value = "@material/mwc-slider", version = "0.27.0")
@JsModule("@material/mwc-slider/slider.js")
public class MwcSlider extends AbstractSinglePropertyField<MwcSlider, Integer> {

    private static final PropertyDescriptor<Boolean, Boolean> discreteProperty = PropertyDescriptors
            .propertyWithDefault("discrete", false);

    public MwcSlider() {
        super("value", 0, false);
    }

    @Synchronize("change") // synchronize value onChange event
    @Override
    public Integer getValue() {
        return super.getValue();
    }

    public Registration addClickListener(
            ComponentEventListener<ClickEvent> listener) {
        return addListener(ClickEvent.class, listener);
    }

    public void setDiscrete(boolean discrete) {
        discreteProperty.set(this, discrete);
    }

    public boolean isDiscrete() {
        return discreteProperty.get(this);
    }

    public void layout(boolean skipUpdateUI) {
        getElement().callJsFunction("layout", skipUpdateUI);
    }

    @DomEvent("click")
    public static class ClickEvent extends ComponentEvent<MwcSlider> {

        private int x, y;

        public ClickEvent(MwcSlider source, boolean fromClient,
                @EventData("event.offsetX") int x,
                @EventData("event.offsetY") int y) {
            super(source, fromClient);
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

    }

}
```

You can extend this class further to support additional configuration properties, such as `min` and `max`.

## <a id="adding-sub-elements-to-define-child-content"></a>Adding Sub-Elements to Define Child Content

Some Web Components can contain child elements. If a component is a layout type and you only want to add child components, implementing the `HasComponents` interface should be enough. This interface provides default implementations for the `add(Component…​)`, `remove(Component…)` and `removeAll()` methods.

For example, implementing `HasComponents` to implement your own `<div>` wrapper would be done like so:

```java
@Tag(Tag.DIV)
public class Div extends Component implements HasComponents {
}
```

You can then add and remove components using the provided methods. For example, using `add()` methods provided by the `HasComponents` interface like so:

```java
Div root = new Div();
root.add(new Span("Hello"));
root.add(new Span("World"));
add(root);
```

If you don’t want to provide a public `add`/`remove` API, you have two options: use the `Element` API, or create a new `Component` to encapsulate the internal element behavior.

As an example, assume that you want to create a specialized Vaadin button that can only show a `VaadinIcon`. Below shows how to do that using the available `VaadinIcon` enum that lists the icons in the set:

```java
@Tag("vaadin-button")
@NpmPackage(value = "@vaadin/button", version = "24.10.8")
@JsModule("@vaadin/button/vaadin-button.js")
public class IconButton extends Component {

    private VaadinIcon icon;

    public IconButton(VaadinIcon icon) {
        setIcon(icon);
    }

    public void setIcon(VaadinIcon icon) {
        this.icon = icon;

        Component iconComponent = icon.create();
        getElement().removeAllChildren();
        getElement().appendChild(iconComponent.getElement());
    }

    public void addClickListener(
            ComponentEventListener<ClickEvent<IconButton>> listener) {
        addListener(ClickEvent.class, (ComponentEventListener) listener);
    }

    public VaadinIcon getIcon() {
        return icon;
    }
}
```

The relevant part here is in the `setIcon()` method. `VaadinIcon` happens to include a feature that creates a component for a given icon — the `create()` call — that’s used here to create the child element. After creating the element, all that’s necessary is to attach the root element of the child component by calling `getElement().appendChild(iconComponent.getElement())`.

If the `VaadinIcon.create()` method wasn’t available, you would need to either create the component yourself or use the `Element` API directly. Using the `Element` API, for example, to define the `setIcon()` method would look like this:

```java
public void setIcon(VaadinIcon icon) {
    this.icon = icon;
    getElement().removeAllChildren();

    Element iconElement = new Element("vaadin-icon");
    iconElement.setAttribute("icon", "vaadin:" + icon.name().toLowerCase().replace("_", "-"));
    getElement().appendChild(iconElement);
}
```

The first part here is the same as the previous example. However, in the second part, the element with the correct tag name, `<vaadin-icon>`, is created manually. The `icon` attribute is set to the correct value (as defined in `@vaadin/icons/vaadin-iconset.js`, for example `icon="vaadin:check"` for `VaadinIcon.CHECK`).

After creation, the element is attached to the `<vaadin-button>` element, after removing any previous content.

When using the second approach, you must also ensure that the `vaadin-button.js` dependency is loaded. Otherwise, it’s handled by the `Icon` component class. An example of this would be importing `vaadin-button.js`, `vaadin-iconset.js` and `vaadin-icon.js` like so:

```java
@NpmPackage(value = "@vaadin/button", version = "24.10.8")
@JsModule("@vaadin/button")
@NpmPackage(value = "@vaadin/icons", version = "24.10.8")
@JsModule("@vaadin/icons/vaadin-iconset.js")
@NpmPackage(value = "@vaadin/icon", version = "24.10.8")
@JsModule("@vaadin/icon")
public class IconButton extends Component {
```

You can test either approach in the `AddonView` class. For example, to test the icon button sub-element in the `AddonView` class, you would do something like this:

```java
IconButton iconButton = new IconButton(VaadinIcon.CHECK);
iconButton.addClickListener(e -> {
    int next = (iconButton.getIcon().ordinal() + 1) % VaadinIcon.values().length;
    iconButton.setIcon(VaadinIcon.values()[next]);
});
add(iconButton);
```

This shows the `CHECK` icon and then changes the icon on every click of the button.

> **Note:** You could extend `Button` directly instead of `Component`, but you would then also inherit the entire public `Button` API.

`AACDBA11-3ECD-4E3B-9A36-C64E3963C26C`
