Route Views & Outlets
A route view binds one route model to one component. @virentia/router-react renders the deepest opened view, wraps it in layouts, and exposes child views through an outlet. None of these helpers decide which route is open — they only map opened routes to rendered components.
routeView
routeView connects a route (or router, or virtual route) to a component:
import { routeView } from "@virentia/router-react";
import { homeRoute, profileRoute } from "./router";
import { HomePage } from "./home-page";
import { ProfilePage } from "./profile-page";
const HomeView = routeView({
route: homeRoute,
view: HomePage,
});
const ProfileView = routeView({
route: profileRoute,
view: ProfilePage,
});interface CreateRouteViewProps<Params extends object | void = void> {
route: Route<Params> | Router | VirtualRoute<any, any>;
view: ComponentType;
layout?: LayoutComponent;
children?: RouteView[];
}The view component reads its own params from the route model with useUnit, not from props:
import { useUnit } from "@virentia/react";
function ProfilePage() {
const { id } = useUnit(profileRoute.params);
return <h1>Profile {id}</h1>;
}routesView
routesView renders the deepest opened view from a list. If no view is opened, it renders otherwise (or null):
import { routeView, routesView } from "@virentia/router-react";
export const RoutesView = routesView({
routes: [HomeView, ProfileView],
otherwise: NotFoundPage,
});interface CreateRoutesViewProps {
routes: (RouteView | RouteViewGroup)[];
otherwise?: ComponentType;
layout?: LayoutComponent;
}Nested views and Outlet
Outlet is for parent routes that own a layout while child routes render inside it. The parent view renders <Outlet /> where the child should appear:
import { Outlet, routeView, routesView } from "@virentia/router-react";
const SettingsView = routeView({
route: settingsRoute,
view: () => (
<SettingsLayout>
<Outlet />
</SettingsLayout>
),
children: [
routeView({
route: securityRoute,
view: SecurityPage,
}),
],
});
export const RoutesView = routesView({
routes: [SettingsView],
});Parent/child activation is decided by the route model (a child route opens its parent too). Outlet only chooses where the opened child view is rendered.
Layouts
layout wraps a single route view in a component that receives children:
routeView({
route: profileRoute,
view: ProfilePage,
layout: AppLayout,
});withLayout applies the same layout to a group of views without repeating it:
import { withLayout } from "@virentia/router-react";
const accountViews = withLayout(AccountLayout, [
routeView({ route: profileRoute, view: ProfilePage }),
routeView({ route: securityRoute, view: SecurityPage }),
]);function withLayout(
layout: ComponentType<{ children: ReactNode }>,
views: RouteView[],
): RouteView[];routesView layout
routesView also takes a layout. Unlike a per-view layout, it wraps the whole routesView output, so it is mounted once and stays mounted across route changes — only the inner view remounts. otherwise renders inside it too:
export const RoutesView = routesView({
routes: [OverviewView, ReportsView],
layout: AppShell,
otherwise: NotFoundPage,
});routeViewGroup
withLayout gives every view its own copy of the layout, so switching between those views remounts the layout and loses its state. routeViewGroup shares one layout across several views: it combines their routes into a group and carries the shared layout. It does not render anything itself — routesView renders it, keeping the layout mounted while any member is active and swapping only the inner view. The layout remounts only when navigation leaves the group entirely:
import { routeView, routeViewGroup, routesView } from "@virentia/router-react";
const dashboard = routeViewGroup({
layout: DashboardLayout,
views: [
routeView({ route: overviewRoute, view: OverviewPage }),
routeView({ route: reportsRoute, view: ReportsPage }),
],
});
export const RoutesView = routesView({
routes: [dashboard, routeView({ route: loginRoute, view: LoginPage })],
});interface RouteViewGroup {
route: VirtualRoute<any, any>;
views: RouteView[];
layout?: LayoutComponent;
}
interface CreateRouteViewGroupProps {
views: RouteView[];
layout?: LayoutComponent;
}
function routeViewGroup(props: CreateRouteViewGroupProps): RouteViewGroup;A RouteViewGroup goes in a routesView routes list next to plain views — routesView is the single place that renders. Like route, router and group, build the group at module scope — before any scope is forked — so its group units belong to the forked scope's graph.
Use layout on a routeView for a one-off wrapper, withLayout when sibling views share chrome that may remount, a routesView layout for a shell around the whole view, and routeViewGroup when a shared layout must stay mounted while navigating between its views. An Outlet-based parent route is the better fit when the wrapper itself corresponds to a route with its own state.
Lazy views
lazyRouteView registers the component import as a route preloader and renders it with React.lazy and Suspense:
import { lazyRouteView } from "@virentia/router-react";
const ProfileView = lazyRouteView({
route: profileRoute,
view: () => import("./profile-page"),
fallback: ProfileSkeleton,
});interface CreateLazyRouteViewProps<Params extends object | void = void>
extends Omit<CreateRouteViewProps<Params>, "view"> {
view: () => Promise<{ default: ComponentType }>;
fallback?: ComponentType;
}Registering the import as a preloader means the route waits for the chunk before activation finishes, so navigation does not flash an empty screen. fallback renders through Suspense while the chunk loads.
This covers the component import only — it is not a data-loading protocol. For lazy business or data models, use lazyModel from @virentia/core and start units from route events, commands, or beforeOpen.