Skip to content

RouterView(router, depth?) subscribes to router.current and renders the component whose matched route sits at depth in the match chain. Lazy components (() => import(...)) are resolved on first match; swaps between routes call the previous component’s dispose().

import { RouterView } from "@whisq/router";
function RouterView(router: Router, depth?: number): WhisqNode;
ParamTypeDescription
routerRouterThe router returned by createRouter.
depthnumber (optional, default 0)Which level of the match chain to render. 0 = the top-level route; 1 = its child; etc.

Most apps have exactly one top-level RouterView at the app root:

import { createRouter, RouterView } from "@whisq/router";
import { mount } from "@whisq/core";
const router = createRouter({
routes: [
{ path: "/", component: Home },
{ path: "/about", component: About },
],
});
mount(RouterView(router), document.getElementById("app")!);

A parent route declares children, and its component renders a RouterView at the next depth:

App.ts
mount(RouterView(router), document.getElementById("app")!);
routes.ts
const routes: RouteConfig[] = [
{
path: "/users",
component: UsersLayout, // renders at depth 0
children: [
{ path: "", component: UsersList }, // /users → depth 1
{ path: ":id", component: UserDetail }, // /users/:id → depth 1
{ path: ":id/edit", component: UserEdit }, // /users/:id/edit → depth 1
],
},
];
UsersLayout.ts
import { component, div, nav } from "@whisq/core";
import { RouterView, Link } from "@whisq/router";
export const UsersLayout = component(() =>
div(
nav(
Link({ href: "/users", router }, "All"),
Link({ href: "/users/new", router }, "New"),
),
RouterView(router, 1), // renders matched child
),
);

Each nested route’s params are available to its component via the render callback (via the first argument, which is Record<string, string>). router.current.value.params aggregates all params from every matched route.

RouterView returns a WhisqNode, so it composes with transition() like any other element:

import { transition } from "@whisq/core";
transition(
RouterView(router),
{
enter: { opacity: [0, 1], duration: 150 },
exit: { opacity: [1, 0], duration: 150 },
},
)

Because the underlying DOM node is a single <div data-whisq-router-view> whose children swap on route change, outer transitions animate the container and not individual routes. For per-route animations, wrap each matched component in its own transition() call.

  • Mount. RouterView attaches an effect on router.current. First render resolves the matched component (awaits the import(...) if lazy) and appends its DOM node.
  • Route change. The previous component’s dispose() is called, the container is cleared, the new component is resolved and mounted.
  • Stale resolutions are discarded. If a route changes while a lazy component is still resolving, the resolution’s output is dropped (identity check against router.current.value).
  • Unmount. Call node.dispose() on the returned WhisqNode to tear down the effect and the current component. In practice you’d do this alongside router.dispose() during HMR or test teardown.

Docs current to v0.1.0-alpha.9 . All releases →