> Markdown version of [Routing](https://vaadin.com/docs/latest/hilla/guides/routing). Section index: [llms.txt](https://vaadin.com/docs/latest/hilla/llms.txt)

# Routing

This documentation guides you through the basics of routing in your application.

## <a id="adding-routes"></a>Adding Routes

The router is using the `.tsx` (and `.jsx`) files in the `src/main/frontend/views/` directory and its subdirectories as routes.

> **Note: The default frontend source directory.**
>
> The default frontend source directory location is `src/main/frontend`. The old default location, the `frontend` directory in the root of the application project, is also supported.

To add a new route to an application, create a new component file that should be displayed when the route is active. For example, create a file called `example.tsx` in the `views` directory with the following content:

```tsx
export default function ExampleView() {
  return <div>My first view</div>;
}
```

Now, after saving the file, you should be able to navigate to `http://localhost:8080/example` and see the new component rendered in the browser.

To add a simple navigation link that points to your new route, use the `<NavLink>` component from `react-router`:

```tsx
import { NavLink } from 'react-router';

<NavLink to="/my-view">My View</NavLink>
```

This creates a clickable link in your application that navigates to the specified route.

### <a id="filename-conventions"></a>Filename Conventions

The Hilla router supports the following naming conventions for the `.tsx`/`.jsx` files in `views` and any nested subdirectories:

- `_` at the start — ignore the file

  The router ignores any files named starting with the underscore character, for example, `_utils.tsx`.

- `@index.tsx` — directory index view

  Files named `@index.tsx` are mapped to the index of the directory. For example, the `views/@index.tsx` file will be mapped to the root URL of the application (`/`). Similarly, `views/some-dir/@index.tsx` file will be mapped to the `/some-dir/`.

- `@layout.tsx` — directory layout

  Files named `@layout.tsx` define the layout (view wrapping component) for the other views defined in the same directory or its subdirectories. The layouts are further described in the [Adding Layout Routes](#adding-layout-routes) section.

- `{parameterName}.tsx` — parameter mapping

  The file name part between the braces is mapped to a required route parameter, as described in [Adding Routes with Parameters](#adding-routes-with-parameters) below.

- `{{optionalParameter}}.tsx` — optional parameter mapping

  Same as above, but the mapped route parameter is optional.

- `{...wildcard}.tsx` — wildcard mapping

  Creates a route mapping for the wildcard parameter (`"*"`), which matches any characters.

## <a id="adding-routes-with-parameters"></a>Adding Routes with Parameters

Sometimes, you may need to create routes that accept dynamic parameters, such as product IDs or names. This can be done by using one of the parameter file name conventions: `{parameter}.tsx`, `{{optionalParameter}}.tsx`, or `{...wildcard}.tsx`.

For example, create a file named `{productId}.tsx` in the `src/main/frontend/views/products/` directory with the following content:

`{productId}.tsx`

```tsx
import { useParams } from 'react-router';

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

  // Fetch product details for the given ID
  const product = useSignal<Product | undefined>(undefined);

  useEffect(() => {
    ProductService.getProduct(productId).then(p => product.value = p);
  }, [productId]);}

  // ...
}
```

This creates the route with the path `/products/{productId}`, where `{productId}` acts as a placeholder for the actual product ID. To access the route parameter in the view, use the `useParams` hook from `react-router` in your component, as shown in the example above. The view component fetches product data for the product ID in the route.

To link to this route with a specific `productId`, you can use the `to` property of `NavLink` like this:

```tsx
<NavLink to="/products/123">Show product details</NavLink>
```

> **Note: The wildcard parameter name**
>
> Due to a limitation in React Router, the wildcard parameter is always named `*` in the `useParams()` result. Use a destructuring rename for convenience:
>
> `{...wildcard}.tsx`
>
> ```tsx
> export default function WildcardView() {
>   const { "*": wildcard } = useParams();
>   // ...
> }
> ```
>
> The file name part following the three dots (as in, for example, `{...name}.tsx`) is ignored.

## <a id="adding-layout-routes"></a>Adding Layout Routes

Layouts are special view components that wrap around other views. It is common for applications to have at least one layout: the main layout that renders the toolbar, the main menu, and similar functionality.

For adding a layout, create a view file named `@layout.tsx` in the views directory, and render the `<Outlet/>` in the component. You can also use the [`App Layout`](https://vaadin.com/docs/latest/components/app-layout.md) component to add common application layout parts.

The following example defines the main layout with the drawer and the main menu:

`@layout.tsx`

```tsx
import { AppLayout } from '@vaadin/react-components/AppLayout.js';
import { DrawerToggle } from '@vaadin/react-components/DrawerToggle.js';
import { Icon } from '@vaadin/react-components/Icon.js';
import { ProgressBar } from '@vaadin/react-components/ProgressBar.js';
import { SideNav } from '@vaadin/react-components/SideNav.js';
import { SideNavItem } from '@vaadin/react-components/SideNavItem.js';
import { Suspense } from 'react';
import { Outlet, useNavigate, useLocation } from 'react-router';

export default function MainLayout() {
  return (
    <AppLayout primarySection="drawer">
      <div slot="drawer" className="flex flex-col justify-between h-full p-m">
        <header className="flex flex-col gap-m">
          <h1 className="text-l m-0">My application</h1>
          <SideNav onNavigate={({path}) => path && navigate(path)} location={location}>
            <SideNavItem path="/example" />
          </SideNav>
        </header>
      </div>

      <DrawerToggle slot="navbar" aria-label="Menu toggle" />

      <Suspense fallback={<ProgressBar indeterminate={true} className="m-0" />}>
        <Outlet />
      </Suspense>
    </AppLayout>
  );
}
```

## <a id="creating-menu-from-routes"></a>Creating Menu from Routes

The structure of application routes is related with navigation menus. The main menu often directly lists the same items as the routes define, thus it could be created from the routes.

The Hilla router offers the `createMenuItems()` utility function to simplify populating the menu using routes data.

The following example demonstrates creating the main menu `createMenuItems()`:

```tsx
import { createMenuItems } from '@vaadin/hilla-file-router/runtime.js';
import { SideNav } from '@vaadin/react-components/SideNav.js';
import { SideNavItem } from '@vaadin/react-components/SideNavItem.js';
import { Outlet, useNavigate, useLocation } from 'react-router';

export default function MainMenu() {
  const navigate = useNavigate();
  const location = useLocation();

  return (
    <SideNav onNavigate={({path}) => path && navigate(path)} location={location}>
      {
        createMenuItems().map(({ to, icon, title }) => (
          <SideNavItem path={to} key={to}>
            {icon && <Icon icon={icon} slot="prefix"/>}
            {title}
          </SideNavItem>
        ))
      }
    </SideNav>
  );
}
```

## <a id="customizing-routes"></a>Customizing Routes

Sometimes, you may want to customize the configuration of a route on top of what is inferred from the file path. By customizing a route you can, for example, set a page title, a menu link title and icon, or override the route path.

To customize the route to a route, in your view source file, export an object named `config` of `ViewConfig` type:

`src/main/frontend/views/about.tsx`

```tsx
import type { ViewConfig } from '@vaadin/hilla-file-router/types.js';

export default function AboutView() {
  return (
    /* ... */
  );
}

export const config: ViewConfig = {
  title: "About Us",
};
```

In this example, a page title is added to the example route.

To access this metadata from within a component, you can use the `useRouteMetadata` hook provided in the starter applications. In the following example, the page title is used to display it in the header of the main layout:

```tsx
import { useRouteMetadata } from 'Frontend/util/routing';

export default function MainLayout() {
  const metadata = useRouteMetadata();
  const currentTitle = metadata?.title ?? 'My App';

  useEffect(() => {document.title = currentTitle;}, [currentTitle]);

  // ...
}
```

Now, when the `/about` route is active, the title `About us` is displayed in the header.

> **Note: Extracting metadata using useMatches**
>
> Under the hood, the route metadata is passed through using the `.handle` React Router route object property. This allows getting the metadata from the `useMatches` hook:
>
> ```ts
> import { useMatches } from 'react-router';
>
> export default function MainLayout() {
>   const matches = useMatches();
>   const currentHandle = matches[matches.length - 1]?.handle as any;
>   const currentTitle = currentHandle?.title ?? 'My App';
>
>   useEffect(() => {document.title = currentTitle;}, [currentTitle]);
>
>   // ...
> }
> ```

### <a id="view-config-options-reference"></a>View Config Options Reference

Here are the options supported in the `config: ViewConfig<T>` object (all of them are optional):

- `title: string`

  View title for use in the main layout header, in the browser window `document.title`, and as the default for the menu entry. If not defined, the component name is used.

- `route: string`

  Overrides the route path configuration. Uses the same syntax as the `path` property with React Router.

- `loginRequired: boolean`

  For applications using authentication, requires user authentication for accessing the view.

- `skipLayouts: boolean`

  Set to `true` to skip all the layouts wrapping for the view. Useful for cases such as the login view that often should not be wrapped in the main layout.

- `rolesAllowed: readonly string[]`

  For applications using authentication, the array of user roles that are allowed to access the view.

- `menu: object`

  The menu item metadata object with the following options:

  - `title: string`

    Title to use in the menu item.

  - `icon: string;`

    Icon to use in the menu.

  - `order: number`

    The number used to determine the order in the menu. Ties are resolved based on the used title. Entries without explicitly defined ordering are put below entries with an order.

  - `exclude: boolean`

    Set to true to explicitly exclude a view from the automatically populated menu.

- `detail: T`

  Optional detailed metadata for the view. This can be used for various purposes, such as displaying extra information in the UI. The type `T` can be any custom type you define.

### <a id="advanced-customization"></a>Advanced Customization

Sometimes, you may want to customize the view metadata even further. The following example shows how to use the `detail` property of `ViewConfig` to add badges to menu items. Both `ViewConfig<T>` and `createMenuItems<T>()` support the `detail` property, which allows you to pass additional metadata for the view. Also, the `useViewConfig` hook can be used to access the current view configuration, including the custom type.

`types/view-detail.ts`

```ts
export default type ViewDetail = { // (1)
  description: string;
  badge: {
    text: string;
    variant: '' | 'success' | 'error' | 'contrast';
  };
```

`views/about.tsx`

```tsx
import type { ViewConfig } from '@vaadin/hilla-file-router/types.js';
import type ViewDetail from 'Frontend/types/view-detail.js';

export default function AboutView() {
  return (
    /* ... */
  );
}

export const config: ViewConfig<ViewDetail> = { // (2)
  title: 'About Us',
  detail: { // (3)
    description: 'Learn more about us',
    badge: {
      text: 'New',
      variant: 'success',
    },
  },
};
```

`views/@layout.tsx`

```tsx
// ...
const viewConfig = useViewConfig<ViewConfig<ViewDetail>>(); // (4)
// ...
<SideNav onNavigate={({ path }) => navigate(path!)} location={location}>
  {createMenuItems<ViewDetail>().map(({ to, title, icon, detail }) => ( {/* <5> */}
    <SideNavItem path={to} key={to}>
      {icon ? <Icon src={icon} slot="prefix"></Icon> : <></>}
      {title}{' '}
      {detail && ( {/* <6> */}
        <span {...{ theme: `badge small ${detail.badge.variant}` }}>{detail.badge.text}</span>
      )}
    </SideNavItem>
  ))}
</SideNav>
// ...
<h1 slot="navbar" className="text-l m-0">
  {viewConfig?.title}{' '}
  <span className="text-s text-secondary">{viewConfig?.detail?.description}</span> {/* <7> */}
</h1>
// ...
```

1. Define a custom type for view details.

2. Use the custom type in the `ViewConfig` definition.

3. Add the `detail` property to the `ViewConfig` object.

4. Use the `useViewConfig` hook to get the current view configuration, including the custom type.

5. Use the custom type in the `createMenuItems` function and get the `detail` property.

6. Use the `detail` property to render a badge in the menu item.

7. Use the `detail` property to display additional information in the header.

## <a id="programmatic-navigation"></a>Programmatic Navigation

Sometimes, you may need to navigate programmatically between routes. For example, this may be needed in response to user interactions or application logic. For this you can use the `useNavigate` hook from `react-router`. It provides a function that allows you to navigate to a specific route when called. Additionally, it offers options to control the navigation behavior, such as pushing to the history stack or replacing the current entry.

For example, after saving a product, you might want to navigate back to the product list:

```tsx
import { useNavigate } from 'react-router';

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

  const handleSave = async () => {
    await ProductService.save(product);
    navigate('/products');
  };

  return (
    <div>
      ...
      <button onClick={handleSave}>Save</button>
    </div>
  );
}
```

By default, this pushes a new entry to the browser’s navigation history. If you want to replace the current entry instead, you can pass `{ replace: true }` as the second argument like so:

```tsx
navigate('/products', { replace: true });
```

## <a id="adding-an-error-page"></a>Adding an Error Page

Adding a custom error page to an application is essential for handling situations in which no other route matches the requested URL. This allows you to give helpful feedback to the user, for example, by communicating the problem or providing links to other pages.

To add an error page (e.g., for 404 not found), create a new route view file for your error page (e.g., `error.tsx`), set the route config to use a wildcard route, and exclude the route from the menu:

`src/main/frontend/views/error.tsx`

```tsx
export default function ErrorView() {
  return <div>Page not found</div>;
}

export const config: ViewConfig = {
  route: '*',
  menu: {
    exclude: true,
  },
};
```

This route matches any unknown routes and display the error page.

Customize the `ErrorView` component to give helpful information to the user.

Now, your application is equipped with an error page that is shown when no other route matches a requested URL.

## <a id="the-routes-tsx-source-file"></a>The `routes.tsx` Source File

The contents of the `routes.tsx` source file determine whether file-based routing, manually added routes for React Router, or the Flow server fallback are enabled.

When the `routes.tsx` source file is not present under `src/main/frontend/`, it’s automatically generated under `src/main/frontend/generated/routes.tsx`. You can copy it to `src/main/frontend/` and customize it as needed. By default, the generated file combines the file routes with the Flow fallback.

> **Note: Using a Custom routes.tsx File**
>
> If you provide a custom `routes.tsx` file (for example, one that manually lists routes using React Router), the Hilla file-system router is not disabled, but its output is ignored. In this case, the files in `src/main/frontend/views` are not used for routing, and you should avoid placing any `.tsx`/`.jsx` files there, as they are still interpreted as views by the Hilla router, resulting in unnecessary processing and possible confusion. To use the new Hilla router, either remove the custom `routes.tsx` file or replace it with a modified copy of the generated file that integrates file-based routing.

## <a id="further-information"></a>Further Information

For more information about routing in Hilla React applications, see the [File-system Router Reference](https://vaadin.com/docs/latest/hilla/reference/react-router.md) article.
