Trying out the file-system router in Vaadin 24.4 beta1

We recently released Vaadin 24.4.0.beta1. One of the new features that we’re introducing is a new way of configuring React Router based on a hierarchy of files in the file system rather than through code in routes.tsx.

Here’s how you can try that out right now. Start by downloading, extracting it, importing it to an IDE, and running the main method in

The UI in the example project is made up of only three files inside src/main/frontend/views/.

  • @index.tsx is the main view mapped to the root (/) route.
  • @layout.tsxis the main layout in which all views will be rendered.
  • about.tsx is the other view in the application, mapped to the /about route.

The router is automatically configured based on files in src/main/frontend/views/. Additionally, @layout.tsx contains a couple of lines of code to automatically populate the main menu based on the available routes. We know that the automatic menu API is not sophisticated enough to support more complex cases. The intention is that it helps beginners get started but it will be replaced with something specific to the application at some point during development. That’s also why the automatic main menu is rendered from the @layout.tsx file that is included in the application.

Adding a view

You can create a new view by adding a React component in src/main/frontend/views/. To make things slightly more interesting, you can add a file as src/main/frontend/views/unified/@index.tsx. The view can be very simple, e.g. a basic Vaadin Button.

import { Button } from "@vaadin/react-components";

export default function Unified() {
  return <Button>A button</Button>

Based on this file, the menu will be updated to have a “Unified” entry based on the name of the exported function. The router will be configured to have a /unified/ route based on the file location. The default 404 view in development mode will also automatically include the view in the list of views that you can go to.

All files in src/main/frontend/views/ are interpreted as views unless the filename is prefixed with _.

View configuration

You can further configure the view by making the view file export an object named config. The TypeScript type named ViewConfig defines the properties that you can define. As an example, you can add an icon to the menu entry by adding this line of code.

export const config: ViewConfig = {menu: {icon: 'line-awesome/svg/history-solid.svg'}}

Take a look at the ViewConfig type to discover other things that can be configured.

Route parameters

Route parameters are also expressed in the file system structure using specially named files or directories. As a simple example, you can add a src/main/frontend/views/customers/{id}.tsx view with the following content:

import { useParams } from "react-router-dom";

export default function CustomerView() {
  const { id } = useParams();
  return "Customer " + id;

This view requires a dynamic parameter and will therefore not be added to the menu. You can navigate to the view by manually opening the URL http://localhost:8080/customers/123. You can make the parameter optional by renaming the file to {{id}}.tsx. You can make the parameter match multiple path segments by naming the file {{}}.tsx. You can have a parameter in the middle of the path by using the parameter name syntax in the directory name, e.g. src/main/frontend/views/orders/{id}/details.tsx that would match a route like http://localhost:8080/orders/123/details.

Sub layouts

The application’s main layout is implemented by src/main/frontend/views/@layout.tsx. You can also add sub layouts to specific sections of the application by putting a @layout.tsx file in a sub directory. Views under that directory will then be rendered inside that layout which is in turn rendered inside the main layout.

As an example, you can create src/main/frontend/views/unified/@layout.tsx with this content:

import { Outlet } from "react-router-dom";

export default function UnifiedLayout() {
  return <div style={{border: '1px solid black'}}><Outlet /></div>