Routing and Navigation

This section documents the current Vaadin release line — Vaadin 24 LTS / 25.x, Java 17+, Spring Boot 3 / Jakarta EE 10 — as published at the official Vaadin documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Flow (server-side Java) is the authoring style used throughout, with Hilla / React shown where it differs; Vaadin 7 and the pre-Flow architecture appear only as migration contrast.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since Vaadin ships major releases roughly twice a year and its ecosystem iterates.

This section’s bibliography lists the reference material consulted while preparing these pages.

In Flow a view is a component class annotated with @Route; the framework builds a URL-to-class map at startup and swaps views into a layout on navigation, all server-side. This page follows Routing & Navigation and Views & Navigation. It replaces the Vaadin 7 Navigator / View API entirely.

Mapping views

@Route("customers")            // https://.../customers
public class CustomerListView extends VerticalLayout { }

@Route(value = "", layout = MainLayout.class)   // the application root
public class DashboardView extends VerticalLayout { }

@Route("about")
@RouteAlias("info")            // a second path to the same view
public class AboutView extends Div { }

Omitting value derives the path from the class name (CustomerListViewcustomer-list). Register or inspect routes at runtime with RouteConfiguration.

add(new RouterLink("Customers", CustomerListView.class));

// from an event handler
UI.getCurrent().navigate(CustomerListView.class);
UI.getCurrent().navigate("customers/42");

RouterLink renders an <a> that Flow intercepts, so navigation stays a single round trip with no full page load.

Route parameters

Three mechanisms, from simplest to most flexible:

// 1. A single typed parameter
@Route("customer")
public class CustomerView extends Div implements HasUrlParameter<Long> {
    @Override
    public void setParameter(BeforeEvent event, Long id) {
        show(customerService.findById(id));   // /customer/42
    }
}

// 2. Named parameters in a route template
@Route("order/:orderId/line/:lineId")
public class OrderLineView extends Div implements BeforeEnterObserver {
    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        String orderId = event.getRouteParameters().get("orderId").orElseThrow();
    }
}

// 3. Query parameters
@Override
public void beforeEnter(BeforeEnterEvent event) {
    QueryParameters qp = event.getLocation().getQueryParameters();
    String tab = qp.getParameters().getOrDefault("tab", List.of("summary")).get(0);
}

Template segments support modifiers: :id? (optional), :path* (wildcard), and typed patterns such as :id(\\d+). See Route parameters.

The navigation lifecycle

Every navigation runs three phases. Observers implemented on the view — or registered globally with UI.addBeforeEnterListener(…​) — can inspect the target, redirect, or defer.

flowchart TD start([navigate to target]) --> leave["BeforeLeave on the CURRENT view
(BeforeLeaveObserver)"] leave -->|"postpone()"| pending["navigation paused
e.g. 'discard unsaved changes?'"] pending -->|"proceed()"| enter leave -->|no postpone| enter["BeforeEnter on the TARGET view
(BeforeEnterObserver)"] enter -->|"rerouteTo(...) / forwardTo(...)"| reroute["restart with a new target"] reroute --> leave enter -->|allowed| render["view attached to the layout"] render --> after["AfterNavigation
(AfterNavigationObserver) — URL is updated"] after --> done([done])
public class EditorView extends Div implements BeforeLeaveObserver {
    @Override
    public void beforeLeave(BeforeLeaveEvent event) {
        if (binder.hasChanges()) {
            BeforeLeaveEvent.ContinueNavigationAction action = event.postpone();
            confirmDiscard(action::proceed);
        }
    }
}

// redirect an unauthenticated user
public void beforeEnter(BeforeEnterEvent event) {
    if (!authenticated()) {
        event.rerouteTo(LoginView.class);
    }
}

rerouteTo keeps the URL; forwardTo also updates it. See Navigation lifecycle.

Layouts

A view’s layout wraps it in a shared frame — typically an AppLayout with a navbar and drawer. A layout class implements RouterLayout; @ParentLayout nests one layout in another. Annotating a layout with @Layout makes it the automatic parent of every route that does not set its own.

@Layout                                   // wraps all routes by default
public class MainLayout extends AppLayout implements RouterLayout {
    public MainLayout() {
        addToNavbar(new DrawerToggle(), new H1("My App"));
        addToDrawer(new SideNav());
    }
}

@Route(value = "reports", layout = ReportsLayout.class)   // opt into a different frame
public class ReportsView extends Div { }

Page titles and error views

@Route("customers")
@PageTitle("Customers")                    // static <title>
public class CustomerListView extends VerticalLayout { }

@Route("customer")
public class CustomerView extends Div implements HasDynamicTitle {
    @Override
    public String getPageTitle() {
        return customer.getName() + " — Customers";
    }
}

// a 404 view
@Tag("div")
public class RouteNotFoundView extends Component
        implements HasErrorParameter<NotFoundException> {
    @Override
    public int setErrorParameter(BeforeEnterEvent event,
                                 ErrorParameter<NotFoundException> parameter) {
        getElement().setText("No such page.");
        return HttpStatusCode.NOT_FOUND.getCode();
    }
}

Dynamic routes and menus

RouteConfiguration.forSessionScope().setRoute("path", View.class) registers a route for one session only — useful for feature flags or per-tenant views. MenuConfiguration.getMenuEntries() returns every route annotated with @Menu, which pairs with SideNav to build a navigation drawer without a hand-maintained list:

SideNav nav = new SideNav();
MenuConfiguration.getMenuEntries().forEach(entry ->
        nav.addItem(new SideNavItem(entry.title(), entry.path(),
                entry.icon() == null ? null : new SvgIcon(entry.icon()))));

See also