Vaadin Production Build Issue: Missing Icons

Hi everyone,

I’m encountering an issue with a Vaadin application when building for production. In development mode, all icons display correctly. However, after compiling and deploying the production build, some icons are not visible in the UI.

The icons are located in:
src/main/resources/META-INF/resources/icons

I’ve verified that the images are correctly copied to the output folder:
target/classes/META-INF/resources/icons
They are also used successfully elsewhere in the app (e.g., for PDF generation), so I’m confident the files are included in the build.

Here’s how I use the logo in the UI:

private static final String LOGO_URL = "/icons/icon.png";

Image simSuiteLogo = new Image(LOGO_URL, "SIM SUITE Logo");
simSuiteLogo.setHeight("40px");
simSuiteLogo.getStyle().set("cursor", "pointer");
simSuiteLogo.addClickListener(e -> UI.getCurrent().navigate(""));
Tooltip.forComponent(simSuiteLogo)
       .withText("Torna alla Home")
       .withPosition(Tooltip.TooltipPosition.BOTTOM);

And then I add it to a layout:

HorizontalLayout leftSection = new HorizontalLayout(simSuiteLogo, appTitle, centerLogoContainer);

:white_check_mark: In dev mode: the image shows up correctly.

:x: In production: the image is missing (broken image icon).

I’ve already tried:

  • Clearing browser cache
  • Confirming the file exists in the production build
  • Accessing /icons/icon.png directly in the browser (returns 404)

Any ideas why Vaadin can’t serve resources from META-INF/resources in production mode?
Do I need to configure anything specific for static resources in production?

Thanks in advance!

Just a pointer: did you check your security configuration that the path is allowed to be shown in the browser? (Even tho it would be weird that it would work in dev and not on production)

If you use spring (boot) it might be worth to investigate if it’s possible for you to move your icons to their recommended static resources folder. This would allow spring to handle the static resources without the indirection from Spring → Vaadin → Browser

Edit: another possible difference might be your servlet path if that differs locally vs production accessing something from / (root context) might differ