How to Create a Custom React Component in Vaadin Flow
Learn how to integrate custom React components (Plotly charts) into Vaadin Flow apps. Java backend sends data to a React frontend. React uses hooks to communicate.
Join the DZone community and get the full member experience.
Join For FreeVaadin Flow is a Java-based, backend-driven UI framework that is best suited for admin UIs, where the number of active users is predictable and bounded. Within this controlled context, the UI state can be managed on the backend, sharing only the necessary diffs with the user for rendering.
From a developer’s perspective, all UI configuration remains in Java code. There is no need to manually create separate REST endpoints, as the UI component state is managed directly within Java.
At some point, you may want to integrate custom UI components implemented as standalone libraries, such as charts that are not available in the free (open-source) version of Vaadin Flow. In this article, I would like to share my experience of creating a bridge between Vaadin and JavaScript code that uses React.
In this blog post, I will integrate Plotly, a popular data visualization tool in the Data Science community that is licensed under MIT. Our goal is to create a custom chart and pass data from the backend to render it in the UI.
Architecture
The Vaadin Flow React adapter consists of two parts: a Java component (which extends ReactAdapterComponent
) and a React component written in TypeScript.
From the React component's perspective, we use the useState
hook provided by ReactAdapter from Vaadin Flow.
From the Java perspective, we treat ReactAdapterComponent
as a proxy that follows the default communication flow of the setState
method. All implementation details are abstracted by the Vaadin Flow framework.
Impl
Vaadin Flow utilizes Shadow DOM to encapsulate custom React components, ensuring style isolation and preventing conflicts with the rest of the application. When a React component is integrated using @JsModule
and @Tag
, it is rendered inside a custom element with Shadow DOM enabled, keeping its structure and styles separate from Vaadin’s global styles.
Data binding between Java and the React component is managed via Element.setProperty()
or ReactAdapterComponent
, allowing seamless state synchronization. This approach ensures encapsulated styles, prevents unintended overrides, and maintains a modular UI architecture while enabling dynamic updates between Vaadin and React.
Custom Component
Java Component
The ChartComponent
class is a Vaadin Flow component that integrates Plotly.js for interactive charting in a Java-based web application. It is annotated with @NpmPackage
to ensure that the required JavaScript dependencies — react-plotly.js, plotly.js, and their TypeScript definitions — are included in the project. The @JsModule
annotation links the component to a corresponding React-based implementation defined in chart-component.tsx, enabling seamless frontend integration.
Extending ReactAdapterComponent
, the class provides a way to manage chart data as a JSON structure, facilitating dynamic updates and integration with Vaadin's data-binding mechanisms.
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.react.ReactAdapterComponent;
@NpmPackage(value = "react-plotly.js", version = "2.6.0")
@NpmPackage(value = "plotly.js", version = "3.0.1")
@NpmPackage(value = "@types/react-plotly.js", version = "2.6.3")
@JsModule("./chart-component.tsx")
@Tag("chart-component")
public class ChartComponent extends ReactAdapterComponent {
public void nextValue(ChartComponentValue value) {
setState("value", value);
}
}
public record ChartComponentValue(List<Data> data, Layout layout){
public record Data(String type, List<Integer> x, List<Integer> y){}
public record Layout(int width, int height, String title) {}
}
We specify ChartComponentValue
as a data transfer object that will be serialised by Vaadin and used by Plotly for configuration.
React Component
Create a new file “chart-component.tsx” in “src/main/frontend/.” With custom implementation of ReactAdapterElement
:
import React from 'react';
import Plot from 'react-plotly.js';
import { ReactAdapterElement, RenderHooks } from 'Frontend/generated/flow/ReactAdapter';
class ChartComponent extends ReactAdapterElement {
constructor() {
super();
}
protected override render(hooks: RenderHooks): React.ReactElement | null {
const [data, setData] = hooks.useState<any>('value');
return (
data && (
<Plot
data={ data.data }
layout={ data.layout }
/>
)
);
}
}
customElements.define('chart-component', ChartComponent);
The ChartComponent
class is a React-based web component that integrates Plotly.js for interactive chart rendering within a Vaadin Flow application. It extends ReactAdapterElement
, which allows seamless communication between Vaadin's Java backend and React frontend. The component utilizes the render method, where it initializes a state variable data using hooks.useState
, with an initial placeholder value 'value'.
When valid data is available, it renders a Plot component from the react-plotly.js library, passing the data and layout properties dynamically. Finally, the component is registered as a custom element with the tag name 'chart-component', making it usable within the Vaadin application as a web component.
Disclaimer
Vaadin Flow uses different mechanisms for the dev and production builds of the React component. Remember to test your custom components on the production build, as some integration issues may only become visible during this phase.
Also, if Node.js is present in your build environment, Vaadin will try to download and install it into your home directory (that might not exist in some CI Runners environments).
Conclusion
We've shown how Vaadin Flow’s React adapter can help you add JavaScript tools, like Plotly, to your Java apps. It's clear how the Java part (ReactAdapterComponent
) and the TypeScript React part are kept separate. However, features like Shadow DOM, which are supposed to keep styles separate and safe, often create more headaches for developers. It can make styling and finding bugs harder, not easier, turning what seems helpful into a real challenge. Bringing these two different worlds, Java and JavaScript, together naturally adds some extra complexity compared to just using standard Vaadin. While this connection works, it definitely comes with its own set of difficulties.
Even with these tricky parts, it's good to remember what Vaadin Flow does really well: it lets developers build UIs, especially admin pages, quickly and efficiently, mostly using Java. Think of this React integration as a special tool to use when you really need a particular JavaScript library, not as the everyday way to do things.
Always keep in mind the important warnings: test your custom components carefully in a production-like setup and pay attention to how Node.js behaves within your build systems. If you manage these extra complexities carefully, you will have more options to expand your Vaadin applications, but be prepared for the additional effort it may require.
Opinions expressed by DZone contributors are their own.
Comments