> Markdown version of [Route Templates](https://vaadin.com/docs/next/building-apps/views/pass-data/route-templates). Section index: [llms.txt](https://vaadin.com/docs/next/building-apps/llms.txt)

# Route Templates

In this guide, you’ll learn how to create a view that accepts multiple route parameters using route templates, and how to read those parameters reactively with a [signal](https://vaadin.com/docs/next/flow/ui-state.md). You’ll also learn how to use modifiers and regular expressions to tweak the behavior of the route parameters.

Using a single [route parameter](https://vaadin.com/docs/next/building-apps/views/pass-data/route-parameters.md) is easier than using a route template. If you can get the job done using the `HasUrlParameter<T>` interface, use that instead.

## <a id="copy-paste-into-your-project"></a>Copy-Paste into Your Project

If you want to quickly try out route templates in your Vaadin application, copy-paste the following code into a new Java class named `RouteTemplateView` in your project’s main package:

`RouteTemplateView.java`

```java
@Route("building-apps/pass-data/route-template/:id([0-9]{1,9})/:action?(view|edit)")
public class RouteTemplateView extends VerticalLayout {

    public enum Action {
        VIEW, EDIT
    }

    private static final String PARAM_ID = "id";
    private static final String PARAM_ACTION = "action";

    // Read each parameter reactively from the router state signal
    private final Signal<String> idParam = () -> UI.getCurrentOrThrow()
            .routerStateSignal().get().routeParameters().get(PARAM_ID)
            .orElse(null);

    private final Signal<Action> actionParam = () -> UI.getCurrentOrThrow()
            .routerStateSignal().get().routeParameters().get(PARAM_ACTION)
            .map(value -> Action.valueOf(value.toUpperCase()))
            .orElse(Action.VIEW);

    RouteTemplateView() {
        Paragraph idParagraph = new Paragraph();
        Paragraph actionParagraph = new Paragraph();
        add(idParagraph, actionParagraph);

        add(new Button("Set Parameter Values",
                e -> showView((int) (Math.random() * 1000),
                        Math.random() < 0.5 ? Action.VIEW : Action.EDIT)));

        // Bind the paragraphs to the parameter signals
        idParagraph.bindText(() -> "ID: " + idParam.get());
        actionParagraph.bindText(() -> "Action: " + actionParam.get());
    }

    // For use by other views navigating to this view
    public static void showView(int id, Action action) {
        var parameters = new RouteParameters(new RouteParam(PARAM_ID, id),
                new RouteParam(PARAM_ACTION, action.name().toLowerCase()));
        UI.getCurrentOrThrow().navigate(RouteTemplateView.class, parameters);
    }
}
```

This view reads its route parameters from the router state signal and binds a `Paragraph` to each, so the displayed values update automatically whenever the URL changes. For more detailed instructions on how to use route templates, continue reading below.

## <a id="specifying-multiple-route-parameters"></a>Specifying Multiple Route Parameters

To specify multiple route parameters, you use a *route template* instead of a static path when declaring a `@Route`. In a route template, one or more of the URL segments is a route parameter. Route parameters must follow the following syntax:

`:parameter_name[modifier][(regex)]`

The following sections explain this syntax in detail.

The following example demonstrates a view with two route parameters: `customerId` and `action`:

```java
@Route("customer/:customerId/:action")
public class CustomerView extends Main {
    ...
}
```

Now, if you navigate to `/customer/cu12345/edit`, the router renders the `CustomerView` with the following route parameter values:

- `customerId` → `"cu12345"`

- `action` → `"edit"`

## <a id="accessing-route-parameter-values"></a>Accessing Route Parameter Values

The current route parameters are available as a [signal](https://vaadin.com/docs/next/flow/ui-state.md). Calling `UI.getCurrentOrThrow().routerStateSignal()` returns a read-only signal whose value is a `RouterState` record; its `routeParameters()` method gives you the `RouteParameters` for the active navigation.

A convenient pattern is to derive a separate signal for each parameter. Because `Signal` is a functional interface, you can define one with a lambda that reads the value from the router state:

```java
private static final String PARAM_CUSTOMER_ID = "customerId"; // (1)
private static final String PARAM_ACTION = "action";

private final Signal<String> customerIdParam = () -> UI.getCurrentOrThrow()
        .routerStateSignal().get().routeParameters().get(PARAM_CUSTOMER_ID)
        .orElse(null);
```

1. **Tip:** To improve readability and maintainability, declare route parameter names as constants near the `@Route` annotation.

Bind components to these parameter signals as you would to any other signal — for example, with `bindText()`:

```java
customerIdParagraph.bindText(() -> "Customer ID: " + customerIdParam.get());
```

The value is read inside the binding’s reactive context, so the paragraph re-renders automatically whenever the parameter changes — including when the view navigates to itself with new values. The `RouteParameters` class defines methods for reading values as `String`, `Integer`, or `Long`; they all return an `Optional<T>`. The sections below show just the relevant accessor call on `routeParameters` — that is, the body of each parameter signal’s lambda.

> **Important:** Read each parameter defensively, supplying a default with `orElse()` rather than `orElseThrow()`. A binding is first evaluated while the view is being constructed — before the router has populated the state with the navigation’s parameters — so even a required parameter is briefly absent. Returning a default keeps that first evaluation harmless, and the binding re-runs with the real value once navigation completes. (A missing *required* parameter in the URL itself still yields a `404 Not Found`: navigating to `/customer` when the route requires more segments simply fails to match.)

> **Note:** The router state signal is read-only. To change the parameters, navigate to the view with new values, as the `showView()` method in the copy-paste example does. If you need to inspect parameters imperatively — for example, to redirect before the view renders — implement `BeforeEnterObserver` and read `event.getRouteParameters()` inside `beforeEnter()`.

## <a id="optional-route-parameters"></a>Optional Route Parameters

By default, all route parameters are required. To make a route parameter optional, use the `?` modifier:

```java
@Route("customer/:customerId/:action?") // (1)
```

1. The `action` route parameter is now optional.

The route parameter value can now be empty, so read it with a default:

```java
routeParameters.get(PARAM_ACTION).orElse(null)
```

Now, if you navigate to `/customer/cu12345`, the `action` route parameter is empty. You can handle empty parameters by providing a default value, redirecting users, or displaying an error message. To redirect before the view renders, call `forwardTo()` or `rerouteTo()` in `beforeEnter()`. See [Navigation Lifecycle](https://vaadin.com/docs/next/flow/routing/lifecycle.md#rerouting) for details.

When using multiple optional route parameters, values are assigned from left to right. If a parameter is missing, the next available value shifts left to fill its place. For instance, consider an application with the route `customer/:customerId?/:action?`:

- `/customer` → Calls the `customer` route with no route parameters at all.

- `/customer/cu12345` → Calls the `customer` route with `"cu12345"` as the value of `customerId`.

- `/customer/cu12345/edit` → Calls the `customer` route with `"cu12345"` as the value of `customerId`, and `"edit"` as the value of `action`.

## <a id="wildcard-route-parameters"></a>Wildcard Route Parameters

By default, a route parameter captures only a single URL segment. However, the last segment in the route template can be declared a wildcard route parameter. This means that the route parameter captures all the segments in its place. A wildcard route parameter is always optional.

To denote a route parameter a wildcard, use the `*` modifier:

```java
@Route("api/:path*")
```

Read the captured path as a single string:

```java
routeParameters.get("path").orElse("")
```

Now, if you navigate to `api/com/vaadin/flow/`, the `path` route parameter has the value `"com/vaadin/flow"`.

You can also access the URL segments captured by a wildcard route parameter individually with `getWildcard()`:

```java
routeParameters.getWildcard("path")
```

Now, if you navigate to the same URL, `getWildcard()` returns the list `["com", "vaadin", "flow"]`.

> **Note:** If a route parameter is missing, `getWildcard()` returns an empty list.

## <a id="constraining-route-parameter-values-with-regular-expressions"></a>Constraining Route Parameter Values with Regular Expressions

In all the examples discussed, the route parameters accept any value. However, a specific value is often expected for a route parameter and the view should be shown only when that specific value is present in the URL. You can do this by defining a regular expression for the route parameter. This reduces the need for validation and sanitation of route parameter values in your view.

> **Note:** The syntax of the regular expressions is checked at application startup. If there is an error, the application fails to start.

In the following example, the `customerId` route parameter is constrained to an integer between 1 and 9 digits, and the `action` route parameter can be either `"view"`, `"edit"`, or empty:

```java
@Route("customer/:customerId([0-9]{1,9})/:action?(view|edit)")
public class CustomerView extends Main {
    ...
}
```

If you navigate to a URL that doesn’t meet these constraints, you’ll receive a `404 Not Found` error.

When you specify constraints on wildcard route parameters, the regular expression is applied to every segment that would be captured by the route parameter. If any of the segments fails to match the expression, the whole route template fails to match the URL, and you’ll get a `404 Not Found` error.
