Docs

Documentation versions (currently viewingVaadin 24)

Route Parameters

In this guide, you’ll learn how to create a view that accepts a single route parameter. You’ll also explore the differences between required, optional, and wildcard route parameters.

Required Route Parameters

To create a Hilla view that accepts a required route parameter, you need to define the parameter name as the view name placed between curly braces ({}) followed by the .tsx suffix. For example, to create a view that accepts a required productId parameter, you should name the view {productId}.tsx. The directory structure under the views looks like this:

views/
└── products/
    └── {productId}.tsx (1)
  1. The view file that accepts the productId parameter. The route is in the form of /products/{productId}, where productId is a required parameter. Routes such as /products/123, /products/prd222, etc., match this view. Trying to navigate to /products results in a 404 Not Found error unless a view matching the route is defined.

Though it is technically possible to define the views with parameters directly under the views directory, it is recommended to use subdirectories to keep the views organized.

The routing system also supports having parameters in the middle of the route, which means the directories can also have their names defined with a {param-name} convention. Here is an example of a view that accepts a required productId parameter:

views/
└── products
    └── {productId} (1)
        └── edit.tsx
  1. The directory where all its children files have the productId parameter in the middle of their routes. The route is in the form of /products/{productId}/edit, where productId acts as a placeholder for the required parameter, in this case, the product ID. Routes such as /products/123/edit, /products/prd222/edit, etc., match this view. Trying to navigate to /products/edit results in a 404 Not Found error unless a view matching the route is defined.

Accessing Route Parameter Value

To access the route parameter in the view, use the useParams hook from react-router in your component. Here is an example of a view that accepts a required productId parameter:

import { useParams } from 'react-router';

export default function ProductView() {
  const { productId } = useParams(); (1)

  return (
    <>
        <h1>Product Details</h1>
        <p>Product ID: {productId}</p>
    </>
  );
}
  1. The useParams hook returns an object containing the route parameters. In this case, the productId parameter is extracted from the object and stored in a variable.

Passing Route Parameter

To navigate to a view that accepts a route parameter, use the NavLink component from react-router. Here is an example of how to create a link to a view located at /products/123:

import { NavLink } from 'react-router';
...
<NavLink to="/products/123">Product 123</NavLink>

Another way is to use the useNavigate hook to navigate programmatically. Here is an example of how to use useNavigate for programmatic navigation:

import { useNavigate } from 'react-router';

function MyComponent() {
  const navigate = useNavigate();

  const handleClick = () => {
    navigate('/products/123');
  };

  return (
    <button onClick={handleClick}>
      Go to Product 123
    </button>
  );
}

By using either of the above methods, you can navigate to the /views/products/{productId}.tsx view that accepts the productId route parameter. If you navigate to /products/123, the productId parameter is extracted and used in the view to fetch the product details for the given ID.

If a parameter is expected in the middle of the route, the method for navigating to the view is the same. For example, to navigate to /products/123/edit, use the same methods as above. The useParams hook extracts the productId parameter from the route, and the view fetches the product details for the given ID.

Optional Route Parameters

Views can also have optional route parameters. This means that the view can be navigated to with or without the parameter. To make a route parameter optional, use double curly braces around the parameter name when naming the view file. For example, to create a view that accepts an optional categoryName parameter, name the view {{categoryName}}.tsx. The directory structure under the views looks like this:

views/
└── products
    └── {{categoryName}}.tsx (1)
  1. The view file that accepts the categoryName optional parameter. The route for this view is in the form of /products/{{categoryName}}, where categoryName is an optional parameter. Routes such as /products, /products/electronics, /products/clothing, etc., match this view. Note that a view with an optional parameter should be able to handle both cases when the parameter is present and when it is not. Here is an example of a view that accepts an optional categoryName parameter:

import { useParams } from "react-router";
import { ProductService } from "Frontend/generated/endpoints.js";
import { useEffect } from "react";
import { useSignal } from "@vaadin/hilla-react-signals";

export default function ProductByCategoriesView() {
  const { categoryName } = useParams(); (1)
  const products = useSignal<string[]>([]);

  useEffect(() => {
    if (categoryName == undefined) { (2)
      ProductService.allProducts().then((data) => products.value = data);
    } else {
      ProductService.productsInCategory(categoryName).then((data) => products.value = data);
    }
  }, []);

  return (
    <>
      <h3>Products from {categoryName ? `'${categoryName}' category` : "all categories"}:</h3>
      <div>
        <ul>{products.value.map((product) => (
          <li key={product}>{product}</li>
        ))}</ul>
      </div>
    </>
  );
}
  1. The useParams hook returns an object containing the route parameters. In this case, the categoryName parameter is extracted from the object and stored in a variable.

  2. The categoryName parameter is checked to determine whether it is present or not. If it is not present, all products are fetched. Otherwise, products in the specified category are fetched.

In the above example, the ProductByCategoriesView fetches all products when the categoryName parameter is not present. When the categoryName parameter is present, it fetches the products in the specified category. The view displays the products in the specified category or all products if the categoryName parameter is not present.

Wildcard Route Parameters

Wildcard route parameters are used to match any number of URL segments. This means when a URL cannot be matched with the other defined routes, the wildcard route is picked as the fallback to handle the navigation. One of the common cases of defining wildcard route parameters when defining the routes in a Hilla application is to properly handle the navigation of users when the route is not found. As the default way of defining the routes is through defining the view files and proper directory structure, adding wildcard route parameters should be done by defining the file name as {…​wildcard}.tsx. The literal value wildcard can be anything that is supported by the filesystem as the filename, but Vaadin recommends to use the {…​wildcard}.tsx as a conventional standard to make it more readable and intuitive. An example of a view that accepts a wildcard route parameter is shown below:

views/
├── @index.tsx
├── about.tsx
├── contact-us.tsx
└── {...wildcard}.tsx (1)
  1. The view file that accepts the wildcard route parameter.

The routes that are matched with this view depends on the other defined routes. In this case the /, /about, and /contact-us are mapped to their respective views, and if the user tries to navigate to any other routes such as /123, /orders, or even /about/789, then the {…​wildcard}.tsx is matched as the fallback, accepting the whole unmatched segment of the URL as the wildcard parameter. The view can then handle the navigation and display a custom 404 Not Found page or redirect the user to the home page. Here is an example of a view that accepts a wildcard route parameter:

import { NavLink, useParams } from "react-router";

export default function WildcardView() {
  const wildcard = useParams()['*']; (1)
  return (
    <>
      <h3>Page Not Found!</h3>
      <div>
        The '<b>/{wildcard}</b>' route does not exist.
        Go back to the <NavLink to="/">home page</NavLink>.
      </div>
    </>
  );
}
  1. The wildcard route parameter can be extracted using the useParams hook, which is stored in the params object with the (asterisk) as the key. The matched wildcard parameters can have many segments, and all the segments is extracted at once when reading the params object with the (asterisk) as the key.