With Vaadin, you’re not limited to just Java-based components provided by Vaadin or the Directory — you can seamlessly integrate a wide variety of frontend elements and libraries into your Flow-based application. Whether you’re dealing with native HTML elements, custom Web Components, React libraries, or JavaScript-based widgets, Vaadin provides flexible options to bring everything together in a unified Java UI.
In this article, we’ll walk through different types of components that can be integrated into a Vaadin app and demonstrate each with small code examples.
The code example shown in this article can be found on GitHub.
What types of front-end elements can you integrate?
Let’s look first at the types of frontend elements commonly used in web applications. There are various types of UI building blocks that differ in their technical implementation and origin. Here’s an overview of what you can include in a Vaadin application:
HTML native elements
- Examples:
<button>
,<select>
,<canvas>
- Direct browser support, no framework needed
- Lightweight and fast
Custom Web Components
- Built with standards like Shadow DOM, HTML templates, and JS classes
- Reusable and framework-independent usage
- Clean encapsulation
Framework components (e.g., React)
- Require their own rendering and lifecycle management
- Often use JSX and reactive state
- Cannot be dropped directly into HTML—must be adapted
JavaScript-based components
- Pure JavaScript widgets without custom elements
- Often use
window.init()
and attach methods to elements - Simple, fast—but not encapsulated
Additionally, while not the focus of this article, it's worth noting that Vaadin applications can also incorporate YouTube embeds, portlets, and other widgets.
Integration options in Vaadin – with code examples
1. Native HTML Elements
Vaadin exposes many native HTML elements directly via components like Input
, Div
, Span
, and more. But you can also create any element using the low-level Element
API.
Java example:
Input input = new Input();
NativeButton button = new NativeButton("Click me", event ->
Notification.show("Input value: " + input.getValue()));
add(input, button);
Element canvas = new Element("canvas");
canvas.getStyle().setBorder("1px solid #ccc");
getElement().appendChild(canvas);
2. Custom Web Component
If you have a Web Component (e.g., from webcomponents.org), you can register it and use it in Vaadin with just a few lines of Java and integrate it as a common component to a Vaadin application. You can integrate a Web Component into Vaadin without creating your own JS/TS file if the component is available as an NPM package and fully implements its functionality, including registration and styling.
Java component (ModelViewer.java):
@NpmPackage(value = "@google/model-viewer", version = "v4.1.0")
@JsModule("@google/model-viewer/dist/model-viewer.min.js")
@Tag("model-viewer")
public class ModelViewer extends Component implements HasSize {
public ModelViewer(String src) {
//set attributes of the model viewer
getElement().setAttribute("src", src);
...
}
}
Usage in View:
add(new ModelViewer());
For more information, please check the documentation.
3. React component
Since Vaadin 24.4, you can integrate React components using the ReactAdapterElement
API. To do this, you need an adapter template in a separate .tsx file and a corresponding Java class on the server side. In the template, you extend ReactAdapterElement
, implement the render method, and define the custom element at the end. Here’s how to embed a React slider:
TypeScript (react-slider.tsx):
import Slider from 'rc-slider';
import 'rc-slider/assets/index.css';
class ReactSliderElement extends ReactAdapterElement {
protected render(hooks: RenderHooks): ReactElement {
const [val, setVal] = hooks.useState<number>('sliderValue');
return <Slider value={val} onChange={(v) => setVal(Number(v))} />;
}
}
customElements.define('react-slider', ReactSliderElement);
In the server-side component, you define the NPM dependency using the @NpmPackage
annotation, so you don’t have to add it manually to the package.json
. Additionally, you link to the client-side implementation with the @JsModule
annotation and define the custom element name using the @Tag
annotation.
Java wrapper (ReactSlider.java):
@Tag("react-slider")
@JsModule("./components/react-slider/react-slider.tsx")
@NpmPackage(value = "rc-slider", version = "^11.1.8")
public class ReactSlider extends ReactAdapterComponent {
public Number getValue() {
return getState("sliderValue", Number.class);
}
public void setValue(Number value) {
setState("sliderValue", value);
}
}
In the UI code, you can simply use the React adapter, just like any other Vaadin component, by adding it directly to your layout.
Usage:
add(new ReactSlider());
For more information, please check the documentation.
4. JavaScript-based component/widget
Sometimes, especially with older JS libraries before custom elements became famous, you just want to integrate a plain JavaScript library without the overhead of building a full Web Component. Vaadin allows this via a JavaScript connector approach. Here’s how it works — and how it can be improved with encapsulation or evolved into a full Web Component. Let’s take, for example, a Wheel JS library that implements a spin wheel widget.
i. Basic JS Connector approach
The simplest way is to define a window.initWheel
function in your JavaScript file. This function is globally available and initializes your JS component (e.g., Wheel) on a specific DOM element. You can also attach public methods like spin() directly to the element.
JS Connector (spin-wheel-connector.js):
import {Wheel} from 'spin-wheel';
window.initWheel = (element) => {
const defaultProps = { ... };
const wheel = new Wheel(element, defaultProps);
element.spin = (rotation) => wheel.spinTo(rotation, 3000, null);
};
Java wrapper (SpinWheelWrapper.java):
@NpmPackage(value = "spin-wheel", version = "^5.0.2")
@JsModule("./components/spin-wheel/spin-wheel-wrapper.js")
public class SpinWheelWrapper extends Div implements HasSize {
@Override
protected void onAttach(AttachEvent attachEvent) {
getElement().executeJs("window.initWheel(this)");
}
public void spin() {
getElement().callJsFunction("spin", 3000);
}
}
ii. Web Component integration
When integrating theSpinWheel
as a Web Component into Flow, you also need two parts: a Java file and a JavaScript file. The JavaScript file defines the actual Web Component by registering a custom element like <spin-wheel-component>
. It sets up the DOM structure, initializes the SpinWheel logic, and exposes public methods such as spin()
. Initialization is handled automatically through lifecycle hooks like connectedCallback()
.JavaScript component (spin-wheel-component.js):
import {Wheel} from 'spin-wheel';
class SpinWheelComponent extends HTMLElement {
_props = {
items: [ { label: 'one' }, { label: 'two' }, { label: 'three' } ]
};
connectedCallback() {
this.initWheel();
}
spin(rotation) {
this.wheel.spinTo(rotation ?? 3000, 3000, null);
}
initWheel() {
if (this.wheel) {
this.wheel.init(this._props);
} else {
this.wheel = new Wheel(this, this._props);
}
}
}
customElements.define('spin-wheel-component', SpinWheelComponent);
The Java counterpart wraps this component using annotations like @Tag
and @JsModule
, making it usable in Java views. This approach results in a clean and reusable API that also works outside of Vaadin. Thanks to optional Shadow DOM and attribute monitoring, the component is easy to encapsulate and extend. The trade-off is a bit more boilerplate code, but the outcome is a fully encapsulated and future-proof component
Java component (SpinWheelComponent.java):
@NpmPackage(value = "spin-wheel", version = "^5.0.2")
@JsModule("./components/spin-wheel/spin-wheel-component.js")
@Tag("spin-wheel-component")
public class SpinWheelComponent extends Component implements HasSize {
public void spin() {
getElement().callJsFunction("spin", 3000);
}
}
iii. Choosing the right approach
Approach | Pros | Cons |
---|---|---|
JS Connector | Simple, fast, works well in demos | Global scope, manual init |
Web Component | Encapsulated, reusable, clean API | More setup and structure |
If you need a quick integration, the JS connector is fine. If you want clean encapsulation and reusability, build a Web Component.
For more information, please check the following pages in our documentation:
Conclusion
Vaadin’s flexible API makes it easy to integrate a wide range of components — from native HTML elements to advanced Web Components and React libraries — directly into your Java application. Once integrated, these components behave like regular Vaadin components, with no need to manually touch JavaScript or CSS for everyday use. This allows your team to stay focused on Java while remaining open to modern web technologies, making your project both powerful and future-proof.
Try it yourself
You can find all these examples in this GitHub project: Component Integration Demo App
Explore the code, clone the repo, and see which integration best fits your next project!