Use the Vaadin MCP tools (search_vaadin_docs, get_component_java_api, get_component_styling) to look up the latest documentation whenever uncertain about a specific API detail. Always set vaadin_version to "25" and ui_language to "java".
For an exact Java API signature or to read source, use the javadoc MCP (mcp__javadoc__* — find via ToolSearch javadoc if not loaded) to read Javadoc and sources from Maven Central instead of unpacking jars from ~/.m2.
When NOT to Use HorizontalLayout / VerticalLayout
Before reaching for these layouts, consider whether a different component is a better fit:
Use AppLayout instead when building the application shell — the top-level structure with a navigation drawer, header bar, and main content area. AppLayout handles responsive drawer collapsing, hamburger menus on mobile, and navbar placement automatically. Building this with nested HorizontalLayout/VerticalLayout is a common anti-pattern that produces worse results with more code.
Use Dashboard instead when building a grid of cards or widgets. Dashboard provides automatic responsive reflow, optional user-configurable drag-and-drop rearrangement, and proper grid-based sizing. Nesting HorizontalLayouts with wrapping to simulate a card grid is fragile and doesn't give you the two-dimensional control that a grid-based layout provides.
Use CSS Grid (via custom CSS, or Lumo utility classes if using the Lumo theme) instead when you need a true two-dimensional grid layout — rows AND columns. HorizontalLayout and VerticalLayout are one-dimensional (flexbox). If you find yourself nesting multiple HorizontalLayouts inside a VerticalLayout to create a grid, CSS Grid is the right tool.
Use FormLayout instead when arranging form fields. FormLayout handles responsive column counts, label positioning, and field sizing automatically.
HorizontalLayout and VerticalLayout are the right choice for arranging components in a single row or column — toolbars, button groups, card contents, view sections, and similar one-dimensional arrangements.
FlexLayout: When You Need More Control
FlexLayout provides the same flexbox capabilities as HorizontalLayout/VerticalLayout but with more control: you can change direction at runtime, reverse item order, and configure all flex properties explicitly. However, the Vaadin documentation recommends using HorizontalLayout and VerticalLayout over FlexLayout for most cases. They offer better default styling (gaps, padding) and a more intuitive API. FlexLayout exists primarily for backward compatibility and edge cases where you need to toggle between horizontal and vertical direction dynamically.
Critical Default Behaviors
Understand these defaults before writing any layout code — they cause the most confusion.
VerticalLayout defaults:
- Width: 100% (fills parent)
- Height: undefined (hugs content)
- Spacing: ON
- Padding: ON
HorizontalLayout defaults:
- Width: undefined (hugs content)
- Height: undefined (hugs content)
- Spacing: ON
- Padding: OFF
These asymmetric defaults are the #1 source of layout surprises. When nesting a VerticalLayout inside a HorizontalLayout, the VerticalLayout will try to take 100% of the parent width. Explicitly set sizes when composing layouts.
Alignment
Both layouts are CSS flexbox containers. The axis terminology maps directly:
VerticalLayout (main axis = vertical):
setJustifyContentMode()— controls vertical distribution (START, CENTER, END, BETWEEN, AROUND, EVENLY)setAlignItems()— controls horizontal alignment (START, CENTER, END, STRETCH)
HorizontalLayout (main axis = horizontal):
setJustifyContentMode()— controls horizontal distributionsetAlignItems()— controls vertical alignment (STRETCH is the default here, unlike VerticalLayout)
Override alignment on individual children with layout.setAlignSelf(Alignment.X, child).
HorizontalLayout also offers addToStart(), addToMiddle(), addToEnd() for grouped positioning. These cannot be combined with setJustifyContentMode().
Spacing, Padding, Margin
Spacing — gap between children. Both layouts have it ON by default. Control it with setSpacing(boolean). Fine-tune using theme variants (but don't mix Lumo and Aura theme variants):
layout.setSpacing(false);
layout.getThemeList().add("spacing-xs"); // xs, s, (default), l, xl
Or set a custom pixel value:
layout.setSpacing(12, Unit.PIXELS);
Padding — space between layout border and content. VerticalLayout has it ON by default; HorizontalLayout has it OFF. Use setPadding(boolean).
Margin — space outside the layout border. OFF by default on both. Use setMargin(boolean).
Expanding Items with Flex
Important: setFlexGrow and setFlexShrink are called on the parent layout, passing the child as an argument — not on the child component itself. The flex container controls how its children distribute space; this is a flexbox property of the parent-child relationship, not a property of the child in isolation. There is no component.setFlexGrow(1).
Make a component fill remaining space:
// CORRECT — called on the parent layout, with the child as the second argument
layout.setFlexGrow(1, expandingComponent);
// WRONG — silently does nothing. The child-components varargs parameter is empty,
// so this compiles but applies the flex-grow to no children at all.
layout.setFlexGrow(1);
The child-components parameter is varargs, which means forgetting to pass the child does NOT produce a compile error — it just silently no-ops. If a flex setting appears to have no effect, double-check that you actually passed the target component(s).
Multiple expand ratios work proportionally: layout.setFlexGrow(2, a) and layout.setFlexGrow(1, b) gives a twice the space of b.
Sizing with setWidthFull / setHeightFull (the shrinking trap)
By default in Vaadin 25, setWidthFull() / setHeightFull() / setSizeFull() set literal width: 100% / height: 100% on the component. Inside a flex layout that means "100% of the parent," not "the remaining space." When such a component sits next to a fixed-size sibling, the default flex-shrink: 1 on every child lets both shrink to fit, so the fixed sibling can end up smaller than its specified size.
Two reliable fixes — use either or both:
// Prefer expand()/setFlexGrow over setWidthFull
HorizontalLayout layout = new HorizontalLayout(fixedSizeComponent, fullSizeComponent);
fixedSizeComponent.setWidth("200px");
layout.setFlexGrow(1, fullSizeComponent); // called on layout, not on fullSizeComponent
// Or pin the fixed component so it cannot shrink
HorizontalLayout layout = new HorizontalLayout(fixedSizeComponent, fullSizeComponent);
fixedSizeComponent.setWidth("200px");
fullSizeComponent.setWidthFull();
layout.setFlexShrink(0, fixedSizeComponent); // called on layout, not on fixedSizeComponent
layout.expand(component) is shorthand for layout.setFlexGrow(1, component). With multiple growing children, ratios distribute proportionally: layout.setFlexGrow(2, a) and layout.setFlexGrow(1, b) gives a twice the space of b.
Experimental: Vaadin 25 ships a
layoutComponentImprovementsfeature flag (Flow only) that rewiressetWidthFull/setHeightFull/setSizeFullto applyflex: 1instead of a literal percentage, which prevents fixed-size siblings from shrinking and also setsmin-size: 0on nested layouts. It is off by default — do not assume the behavior unless the user has explicitly enabled the flag.
The Overflow Trap
Components inside layouts can overflow when the content exceeds the layout's constrained size. This commonly causes unwanted scrollbars.
Fix: set minimum size to 0 on the overflowing component:
overflowingComponent.setMinHeight("0");
// or
overflowingComponent.setMinWidth("0");
(The experimental layoutComponentImprovements flag also sets the min-size of nested layouts to 0 when setSizeFull() is used, but that is opt-in — by default you still set it manually.)
Wrapping
HorizontalLayout supports wrapping to prevent overflow:
layout.setWrap(true);
When combining wrapping with addToStart/addToMiddle/addToEnd, group items into sub-layouts for proper wrap behavior.
Scrollable Layouts
When a layout needs to scroll, wrap it inside a Scroller rather than controlling overflow with CSS. Make sure the inner layout does not have an explicit height set — let it hug its content so the Scroller can determine when scrolling is needed. This makes it immediately clear from the code which parts of the UI scroll and which don't.
// GOOD — explicit scrollable area, easy to understand
Scroller scroller = new Scroller();
scroller.setSizeFull();
VerticalLayout content = new VerticalLayout(); // height is undefined (default) — hugs content
content.add(/* many components */);
scroller.setContent(content);
// BAD — scrolling controlled via CSS, not obvious from the code
VerticalLayout content = new VerticalLayout();
content.setSizeFull();
content.getStyle().set("overflow", "auto");
content.add(/* many components */);
Best Practices
- Explicitly disable padding/spacing when not needed — nested layouts accumulate unwanted space fast. Use
setPadding(false)andsetSpacing(false)on inner layouts. - Use the right layout component — AppLayout for app shells, Dashboard for card grids, FormLayout for forms. Don't build everything with HorizontalLayout/VerticalLayout.
- Don't over-nest — a single-child HorizontalLayout just for alignment is unnecessary. Use
setAlignSelf()on the parent instead. - Use Lumo utility classes for spacing fine-tuning (Lumo theme only) —
LumoUtility.Gap,LumoUtility.Paddingprovide more granular control viaaddClassNames(). For Aura, use custom CSS or--vaadin-*-layout-gapproperties. - Use custom CSS properties for global adjustments —
--vaadin-horizontal-layout-gap,--vaadin-vertical-layout-gap,--vaadin-*-layout-padding,--vaadin-*-layout-margin.
Detailed Reference
For a quick-reference cheat sheet of defaults, alignment values, and troubleshooting patterns, see references/layout-cheatsheet.md.