flow icon indicating copy to clipboard operation
flow copied to clipboard

Components not being rendered in Production Mode

Open Bhavith-Kulal opened this issue 9 months ago • 4 comments

In this Forum Post

I mentioned that adding <optimizeBundle>false</optimizeBundle> helped solve that problem.

This is the output log with <optimizeBundle>true</optimizeBundle> and production-profile, providing this for investigative purposes.

Vaadin Version: 24.6.6

Java version: 23.0.1

Apache Maven 3.9.8

maven_output.txt

I have removed some parts of this log( Non - Specific to Vaadin) . Hope this helps.

Bhavith-Kulal avatar Mar 13 '25 12:03 Bhavith-Kulal

We have seen a similar issue several times, so probably there is a pattern in applications that we are not aware of and that causes issues like that. We have to try reproduce it by getting UI components from Spring Context as beans. Looks an impactful topic that we should improve.

mshabarov avatar Mar 18 '25 11:03 mshabarov

I tried to reproduce in several ways but with no success. Unfortunately, also the logs do not help that much since most of the application-specific entries have been removed or redacted.

mcollovati avatar Apr 08 '25 11:04 mcollovati

Minimal reproducible example

import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.CheckboxGroup;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;

@Route
public class MainView extends Div {

  @Autowired
  Supplier<Settings> settings;

  MainView() {
    var container = new Div();
    add(new Button("show", e -> {
      container.removeAll();
      container.add(settings.get());
    }));
    add(container);
  }

  @Component
  static class SettingsSupplier implements Supplier<Settings> {
    @Override
    public Settings get() {
      return new Settings();
    }
  }

  static class Settings extends VerticalLayout {
    public Settings() {
      add(new CheckboxGroup<>("settings", "a", "b", "c"));
    }
  }
}

Steps to reproduce

  1. create a project at https://start.vaadin.com/app
  • select Vaadin 24.7 (latest stable)
  • Java 17 or 21
  • select Pure Java with Vaadin Flow only
  • download and extract the starter
  1. replace the main view with the example code above
  2. build for production: mvn clean install -Pproduction

Important: If you modify the code during testing, be sure to clean frontend artifacts.

rm -rf node_modules package.json package-lock.json vite.generated.ts vite.config.ts src/main/frontend/generated

Observed Behavior

  1. the production build completes without errors
  2. inspecting src/main/frontend/generated/flow/generated-flow-imports.js shows that vaadin-checkbox-group import are missing.
  3. when running the app and clicking the "show" button, the checkbox group fails to render properly.

The browser shows elements missing their shadow DOM.

  • vaadin-button correctly has a #shadow-root node
  • vaadin-checkbox-group and vaadin-vertical-layout do not have shadow roots

Console errors are also visible:

2025-05-16T10:32:42.951+02:00 ERROR 41334 --- [nio-8080-exec-5] c.v.flow.component.internal.UIInternals  : The component class com.vaadin.flow.component.checkbox.CheckboxGroup includes '@vaadin/checkbox-group/src/vaadin-checkbox-group.js' but this file was not included when creating the production bundle. The component will not work properly. Check that you have a reference to the component and that you are not using it only through reflection. If needed add a @Uses(CheckboxGroup.class) where it is used.
2025-05-16T10:32:42.952+02:00 ERROR 41334 --- [nio-8080-exec-5] c.v.flow.component.internal.UIInternals  : The component class *.base.ui.view.MainView$Settings includes '@vaadin/vertical-layout/src/vaadin-vertical-layout.js' but this file was not included when creating the production bundle. The component will not work properly. Check that you have a reference to the component and that you are not using it only through reflection. If needed add a @Uses(Settings.class) where it is used.
Image Image

Root cause analysis

The issue appears to be in how the flow-maven-plugin detects frontend dependencies.

  1. the plugin scans bytecode for direct instantiations of Vaadin components.
  2. it follows reference chains from entry points (like @Route classes)
  3. In this example, components created indirectly through the Autowired Supplier pattern are not detected

The frontend scanner (FrontendClassVisitor) successfully detects direct component creation but seem to fail to recognize components created through dependency injection.

Workaround

@Route
@Uses(CheckboxGroup.class)
@Uses(VerticalLayout.class)
public class MainView extends Div {
  // ...
}

Suggested fix

Enhance the bytecode scanning to detect components created through common dependency injection and factory patterns, particularly when following chains through:

  1. Spring beans
  2. Java functional interfaces (Supplier, Function, etc.)
  3. Factory methods

This would improve developer experience by making component detection more robust without requiring manual @Uses annotations.

muhammetaltindal avatar May 16 '25 09:05 muhammetaltindal

@muhammetaltindal Thanks for your comment!

As you reported, with your example code it is expected that the components are not found by the production build because the Settings class is not reachable by the bytecode scanner, since it is not directly referenced in the MainView code. You are injecting it as a Supplier<Settings>, but due to type erasure, the scanner will only see the Supplier class. In such situation @Uses is currently required.

BTW, @Uses(Settings.class) shoud be enough to fix the issue

mcollovati avatar May 16 '25 09:05 mcollovati