Routing
A View is a React Component exported as an ES module. It is the main building block of any Product and are most commonly known as pages.
The component to be rendered must be the default export of the module.
A Route is a mapping between a URL and a View, and it is used to define the navigation tree of a Product. The App Shell is responsible for loading the View and rendering it at the correct moment and place. On the main panel, it does it according to the active location route.
Example:
Create src/pages/Hello.tsx
with the following code:
export default function Hello() {
return <h1>Hello World!</h1>;
}
Declare the View in the App Shell configuration file:
mainPanel: {
views: [
{
bundle: "@hv-apps/my-app/pages/Hello.js",
route: "/hello",
},
];
}
Now, when launching your application, you can navigate to this View with /hello
.
Check the configuration file reference for more information on how to declare Views.
You can also enable the autoViewsAndRoutes
option in the vite.config.ts
file and when set it will search for Views at the folder specified by the viewsFolder
parameter (src/pages
by default) and add them automatically.
For details on file-based routing, refer to the plugin documentation.
Note that Shared Modules other than Views must still be listed in the modules
parameter at the vite.config.ts
file.
Route Parameters
Routes can contain parameters (the same parameters that react-router supports). The main use-case are the ones defined by a colon followed by the name of the parameter. For example, the route /hello/:name
defines a parameter named name
.
Nested Views
Nested Views allows coupling segments of the URL to the component hierarchy, nesting layouts. It leverages react-router’s nested routes feature.
Nested Views are not supported by the file-based routing feature and must be declared manually in the App Shell configuration file. The automatic and manual definition of Views will be merged.
Example:
Declare it like this in the App Shell configuration file:
mainPanel: {
views: [
{
bundle: "@hv-apps/my-app/pages/PersonBrowser.js",
route: "/persons",
views: [
{ route: "/new", bundle: "@hv-apps/my-app/pages/NewPersonForm.js" },
{ route: "/:id", bundle: "@hv-apps/my-app/pages/PersonDetails.js" },
],
},
];
}
The PersonBrowser
component will receive the nested view in the children
prop, and it can render it at the desired place:
export default function PersonBrowser({ children }) {
return (
<div>
<h1>Person Browser</h1>
<select>
<option>John Doe</option>
<option>Jane Doe</option>
</select>
<div>{children}</div>
</div>
);
}
Why children
instead of using react-router’s <Outlet />
?
The current architectural guidelines, and one of the goals of the App Shell Framework and its plugin, is to keep the exported Shared Modules generally, and Views in particular, uncoupled from the App Shell and its shared dependencies (react-router in this case).
This would allow, for example, to use the same exported ES Module in another context - without the App Shell and react-router - by simply passing it children
.
When loaded by the App Shell, a <Outlet />
instance is automatically passed to the View as children
, so it will work as expected and render the active nested View.
How can I use react-router’s <Outlet />
context and useOutletContext()
?
The Outlet’s context is just an utility. You can create your own context provider and hooks to achieve the same result, benefiting from better type safety and avoiding the coupling to react-router.
But I really want to use <Outlet />
!
If keeping the Shared Modules uncoupled from the App Shell is not a concern, you can use <Outlet />
directly in the View and just ignore the children
prop.
Index Route
For the App Shell the index route, meaning the View that is rendered when the URL matches the parent View route exactly, is just a regular nested View with /
as the route:
mainPanel: {
views: [
{
bundle: "@hv-apps/my-app/pages/PersonBrowser.js",
route: "/persons",
views: [
{ route: "/new", bundle: "@hv-apps/my-app/pages/NewPersonForm.js" },
{ route: "/:id", bundle: "@hv-apps/my-app/pages/PersonDetails.js" },
{ route: "/", bundle: "@hv-apps/my-app/pages/PersonKPIs.js" },
],
},
// ...
];
}
Route | Component |
---|---|
/persons | <PersonBrowser><PersonKPIs> |
/persons/new | <PersonBrowser><NewPersonForm> |
/persons/123 | <PersonBrowser><PersonDetails> |