Blog

How to draw a line between Vaadin components?

By  
Matti Tahvonen
Matti Tahvonen
·
On Jun 3, 2025 4:52:00 PM
·

Interesting question, and I’m sure you are not the first one to have it, I answered a booth visitor at JCON 2025. The Vaadin developer wanted to show a connection between two UI components, to show some sort of relationship between those. I thought that using SVG it ought to be “straightforward” and promised to make some sort of prototype after lunch. In reality it never is (straightforward) when one needs to hop on the browser side, but the first iteration was ready quite soon after the lunch break.

Vaadin Flow provides an SVG component, to draw inline SVG on a web app. A minor drawback is that the lower level Element API still doesn't have proper support for SVG elements, so the graphic must be submitted as a String. Another problem is to figure out the position of the referred elements in the rendered page. Although it is often inspiring to find solutions for interesting problems, probably the best solution is to find a generic library where somebody else has already solved them for you.

Let me present two solutions built from scratch and a third one utilizing a JS library built exactly for this purpose (Java wrapper now available in the Directory).

SVG and some JS to dig the positions

My initial JS based solution was based on these steps:

  • Component is based on simple SVG with line (no points)
  • Component is positioned absolutely, and should be added below other components
  • Add an API whose JS updates the start and the end of the component based on the given components.

The ConnectingLine component looks like this:

static class ConnectingLine extends Svg {
   public ConnectingLine() {
       super("<svg ><line style=\"stroke:red;stroke-width:2\" /></svg>");
       Style style = getStyle();

       // Position the SVG absolutely, so it doesn't disturb actual components

       style.setPosition(Style.Position.ABSOLUTE);
       style.setTop("0");
       style.setLeft("0");
       style.setBottom("0");
       style.setRight("0");
   }

   public void drawLine(Component button1, Component button2) {
       getElement().executeJs("""
                   const w = document.body.clientWidth;
                   const h = this.clientHeight;

                   this.firstChild.setAttribute("viewBox", "0 0 " + w + " " + h );

                   const b1 = $0;
                   const b2 = $1;
                   const line = this.querySelector("line");

                   setTimeout(() => {

                       // some math to determine the center of the buttons and draw the line

                       const rect2 = b2.getBoundingClientRect();
                       line.setAttribute("x2", rect2.x + rect2.width / 2);
                       line.setAttribute("y2", rect2.y + rect2.height / 2);

                       const rect1 = b1.getBoundingClientRect();
                       line.setAttribute("x1", rect1.x + rect1.width / 2);
                       line.setAttribute("y1", rect1.y + rect1.height / 2);
                   }, 0);

               """, button1.getElement(), button2.getElement());
   }
}

A full usage example code that draws a line between buttons is available in GitHub. Note, that although it is in an add-on repository, it is not published, only in the src/test side as a reference. Below you’ll see the end result.

A screenshot of a rudimentary solution with Vaadin Flow’s JS integration.

In pure Java with ResizeObserver

Moments before the original question at our JCON booth, I had hosted a presentation with a 100% Java theme. Thus, the above solution hurt my soul quite a bit. As a workaround I thought I could implement the same with the ResizeObserver tool available in the Viritin add-on. The previously presented tool is very handy to build responsive design logic in Java. After a bit of investigation, I also needed to [add offset coordinates](https://github.com/viritin/flow-viritin/commit/0e12b9d7c610c8934b51b79094e806238a31ca79) to the payload that is reported to the Java callback. After that tiny enhancement, the solution can now be implemented also with 100% pure Java:

private static class ConnectingLine extends Svg {
   private int x1;
   private int y1;
   private int x2;
   private int y2;

   public ConnectingLine(Component component1, Component component2) {
       super("");
       Style style = getStyle();
       style.setPosition(Style.Position.ABSOLUTE);
       style.setTop("0");
       style.setLeft("0");
   }

   private void drawLine(int x1, int y1, int x2, int y2) {
       int width = Math.max(x1, x2);
       int height = Math.max(y1, y2);
       setSvg("""
               <svg viewBox="0 0 %d %d">
                   <line style="stroke:red;stroke-width:2" x1="%d" y1="%d" x2="%d" y2="%d" />
               </svg>
               """.formatted(width, height, x1, y1, x2, y2));
       getStyle().setWidth(width + "px");
       getStyle().setHeight(height + "px");
   }


   public void drawLine(Component component1, Component component2) {
       ResizeObserver.get().observe(component1, dimensions -> {
           this.x1 = dimensions.offsetLeft() + dimensions.offsetWidth() / 2;
           this.y1 = dimensions.offsetTop() + dimensions.offsetHeight() / 2;
       });

       ResizeObserver.get().observe(component2, dimensions -> {
           this.x2 = dimensions.offsetLeft() + dimensions.offsetWidth() / 2;
           this.y2 = dimensions.offsetTop() + dimensions.offsetHeight() / 2;
           drawLine(x1, y1, x2, y2);
       });
   }
}

The “math” is now expressed in the Java code, but technically the solution is very similar. As Vaadin Flow’s Element API doesn't yet support SVG Elements (aka custom namespaces), I’ll simply reset the whole SVG content after the coordinates are calculated.

Integrating LeaderLine

Neither of the above solutions are especially complex, but things may become such easily, when the requirement list starts to grow (I could easily expect these):

  • Could there be a beautiful bezier curve instead of just straight line?
  • We need to support dynamic animations when dragging the component to different location!
  • Could we configure that the line starts different parts of the component?

At this point by latest, one should search if the problem is that unique after all. I quickly found out about the LeaderLine JS library: which essentially draws lines between components. I quickly drafted an add-on project that wraps its functionality for Vaadin Flow users. LLM generated the first draft of the full configuration API, although I needed to spend some time to make it look like the 2025 Java. With the add-on project, you now have a ton of configuration options for example colors, line styling, animations and sockets, and you still stay in that pure Java bubble you love.

Screenshot of the LeaderLine add-on’s demo app, with some weird configuration options. Trust me that the defaults look better 😎

Give the demo a spin, check out the add-on, and see what works best in your app. And if you come up with your own tweaks or use cases, share them on the Vaadin Forum.

Matti Tahvonen
Matti Tahvonen
Matti Tahvonen has a long history in Vaadin R&D: developing the core framework from the dark ages of pure JS client side to the GWT era and creating number of official and unofficial Vaadin add-ons. His current responsibility is to keep you up to date with latest and greatest Vaadin related technologies. You can follow him on Twitter – @MattiTahvonen
Other posts by Matti Tahvonen