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 https://start.vaadin.com/dl?preset=react&preset=partial-prerelease, extracting it, importing it to an IDE, and running the main method in Application.java
.
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.tsx
is 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 {{...id}}.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>
}