App Layout
- Usage
- Styling
- Usage as Root Layout
- Content Area
- Navbar
- Drawer
- Scrolling Behavior
- Bottom Navbar on Small Touchscreens
- Best Practices
App Layout is a component for the root layout of a Vaadin application. It provides predefined areas for the navigation drawer, the header, and the view’s content.
Important
|
Scaled down examples
The examples on this page are scaled down so that their viewport-size-dependent behavior can be demonstrated.
Some examples also change their behavior based on your browser viewport size.
|
new tab
public class AppLayoutBasic extends AppLayout {
public AppLayoutBasic() {
DrawerToggle toggle = new DrawerToggle();
H1 title = new H1("MyApp");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "0");
SideNav nav = getSideNav();
Scroller scroller = new Scroller(nav);
scroller.setClassName(LumoUtility.Padding.SMALL);
addToDrawer(scroller);
addToNavbar(toggle, title);
}
}
The layout consists of three sections:
-
Content area, a view content area;
-
Navbar, a horizontal navigation bar; and
-
Drawer, a collapsible navigation drawer-
An application’s main navigation blocks should be positioned in the navbar
or the drawer
or both, whereas views are rendered in the content area. App Layout is responsive and adjusts automatically to fit desktop, tablet, and mobile screen sizes.
Usage as Root Layout
App Layout is designed to be the application’s root layout, within which most or all views are rendered. It’s not intended to be nested inside other elements.
Flow
With Flow, the root layout can be defined using the @Layout
annotation, which tells the router to render all routes or views inside of it.
@Layout
public class MainLayout extends AppLayout implements RouterLayout {
}
Hilla
Files named @layout.tsx
define the root layout for the other views defined in the same directory or its subdirectories. A @layout.tsx
in the root of the views
directory acts as the default root layout for all views in the application. An <Outlet/>
element is used to tell the router where to render the contents of routes or views.
export default function MainLayout() {
return (
<AppLayout>
<Outlet />
</AppLayout>
);
}
See Hilla routing documentation for details.
Content Area
The content area is where individual views are rendered. The route layout mechanisms in Flow and Hilla can automatically render the contents of routes and views there, but it can be invoked manually:
public class MainLayout extends AppLayout {
public MainLayout() {
MyView view = new MyView();
setContent(view);
}
}
Navbar
The navbar
is a header above the content area. It can contain primary or secondary navigation elements, the application’s title, or view-specific content such as the title of the current view.
@Layout
public class MainLayout extends AppLayout implements RouterLayout {
public MainLayout() {
H1 title = new H1("My App");
addToNavbar(title);
}
}
Navbar Placement
The navbar
can be located on top, or to the side of the drawer
. When put it on top, the navbar
is typically used as an application header. Application headers contain, for example, the application’s name and branding, as well as actions that apply to the entire application (e.g., notifications, settings, etc.).
new tab
public class AppLayoutNavbarPlacement extends AppLayout {
public AppLayoutNavbarPlacement() {
DrawerToggle toggle = new DrawerToggle();
H1 title = new H1("MyApp");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "0");
SideNav nav = getSideNav();
Scroller scroller = new Scroller(nav);
scroller.setClassName(LumoUtility.Padding.SMALL);
addToDrawer(scroller);
addToNavbar(toggle, title);
}
}
When placed to the side, the navbar
is often seen as a view header, containing the view’s title, as well as actions and secondary navigation that relate only to the current view.
new tab
public class AppLayoutNavbarPlacementSide extends AppLayout {
public AppLayoutNavbarPlacementSide() {
DrawerToggle toggle = new DrawerToggle();
H1 title = new H1("Dashboard");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "0");
SideNav nav = getSideNav();
Scroller scroller = new Scroller(nav);
scroller.setClassName(LumoUtility.Padding.SMALL);
addToDrawer(scroller);
addToNavbar(toggle, title);
setPrimarySection(Section.DRAWER);
}
}
Drawer
The drawer
can switch between a fixed area next to the view’s content and an expandable panel, toggled via the drawer toggle. It typically contains the application’s primary navigation, such as a Side Navigation component.
@Layout
public class MainLayout extends AppLayout implements RouterLayout {
public MainLayout() {
SideNav nav = new SideNav();
addToDrawer(nav);
}
}
Scrolling Behavior
Depending on whether App Layout has a defined height, the way the content inside the layout scrolls can differ.
Auto Height
When the App Layout has an undefined or auto height set, which is the default, the <body>
element is the scrolling container for the content inside the layout.
new tab
public class AppLayoutHeightAuto extends AppLayout {
public AppLayoutHeightAuto() {
H1 title = new H1("MyApp");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "var(--lumo-space-m)");
addToNavbar(title);
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");
grid.addColumn(Person::getProfession).setHeader("Profession");
List<Person> people = DataService.getPeople(20);
grid.setItems(people);
grid.setAllRowsVisible(true);
setContent(grid);
}
}
The vertical scrollbar crosses the App Layout navbar
and the content flows under it, allowing for translucent visual styles. Mobile browsers collapse and expand their toolbars when the user scrolls down and up, respectively. On iOS, you can tap the status bar (i.e., where signal strength, battery, and clock are indicated) to scroll back to the top of the page or view.
This behavior isn’t compatible with vertically scrollable Grids, or other scrolling containers within the content area that’s height is 100%. To support those, define 100% height for the App Layout.
Full Height (100%)
To allow a nested component to take all of the available vertical space inside the App Layout, you need to set an explicit height for the layout, typically 100%. A common use case is to let a data grid fill the entire content area.
Note
|
Make sure all parent components and elements have 100% height. The full hierarchy of components from the App Layout to the <body> element need to have 100% height.
|
new tab
public class AppLayoutHeightFull extends AppLayout {
public AppLayoutHeightFull() {
H1 title = new H1("MyApp");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "var(--lumo-space-m)");
addToNavbar(title);
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");
grid.addColumn(Person::getProfession).setHeader("Profession");
List<Person> people = DataService.getPeople();
grid.setItems(people);
setContent(grid);
getElement().getStyle().set("height", "100%");
grid.setHeight("100%");
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
}
}
The vertical scrollbar stays within the layout content area, and mobile browsers don’t collapse their toolbars when the content area is scrolled down.
Bottom Navbar on Small Touchscreens
When the navbar
is used for navigation, the touch-optimized navbar
slot can be used to provide a separate version of the navigation at the bottom of the UI, optimized for mobile phones.
new tab
public class AppLayoutBottomNavbar extends AppLayout {
public AppLayoutBottomNavbar() {
H1 title = new H1("MyApp");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "var(--lumo-space-m) var(--lumo-space-l)");
HorizontalLayout nav = getNavigation();
H2 viewTitle = new H2("View title");
Paragraph viewContent = new Paragraph("View content");
Div content = new Div();
content.add(viewTitle, viewContent);
addToNavbar(title);
addToNavbar(true, nav);
setContent(content);
}
}
Best Practices
Navbar vs Drawer
Choose between navbar
and drawer
based primarily on the number of items placed in it. The navbar
is a good choice for a small number of items (i.e., three to five), as they can fit into the viewport without scrolling.
new tab
public class AppLayoutNavbar extends AppLayout {
public AppLayoutNavbar() {
H1 title = new H1("MyApp");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("left", "var(--lumo-space-l)").set("margin", "0")
.set("position", "absolute");
HorizontalLayout navigation = getNavigation();
navigation.getElement()
addToNavbar(title, navigation);
}
}
When more items need to be displayed, or if small-screen support is a priority, the drawer
is a better choice. It can accommodate a longer list of links without scrolling, and collapses into a hamburger menu on small screens. Furthermore, a vertical list of items is easier for the user to scan.
new tab
public class AppLayoutDrawer extends AppLayout {
public AppLayoutDrawer() {
DrawerToggle toggle = new DrawerToggle();
H1 title = new H1("Dashboard");
title.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "0");
SideNav nav = getTabs();
Scroller scroller = new Scroller(nav);
scroller.setClassName(LumoUtility.Padding.SMALL);
addToDrawer(scroller);
addToNavbar(toggle, title);
setPrimarySection(Section.DRAWER);
}
}
For applications that require multilevel or hierarchical navigation, use the drawer
to contain at least the first level. The secondary and tertiary navigation items can be placed in either the drawer
or the navbar
.
new tab
public class AppLayoutSecondaryNavigation extends AppLayout {
public AppLayoutSecondaryNavigation() {
H1 appTitle = new H1("MyApp");
appTitle.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("line-height", "var(--lumo-size-l)")
.set("margin", "0 var(--lumo-space-m)");
SideNav views = getPrimaryNavigation();
Scroller scroller = new Scroller(views);
scroller.setClassName(LumoUtility.Padding.SMALL);
DrawerToggle toggle = new DrawerToggle();
H2 viewTitle = new H2("Orders");
viewTitle.getStyle().set("font-size", "var(--lumo-font-size-l)")
.set("margin", "0");
HorizontalLayout subViews = getSecondaryNavigation();
HorizontalLayout wrapper = new HorizontalLayout(toggle, viewTitle);
wrapper.setAlignItems(FlexComponent.Alignment.CENTER);
wrapper.setSpacing(false);
VerticalLayout viewHeader = new VerticalLayout(wrapper, subViews);
viewHeader.setPadding(false);
viewHeader.setSpacing(false);
addToDrawer(appTitle, scroller);
addToNavbar(viewHeader);
setPrimarySection(Section.DRAWER);
}
}
3005EA19-8E28-4BF2-8A0A-FC3F46C04F1B