Page Router (LayoutRouter)
An overview of the LayoutRouter page routing API, its nested Layout and Page components, path patterns, and migration from PageRouter.
LayoutRouter works together with Layout and Page components to build a tree-like UI structure. It is the recommended page routing API for menus, settings screens, galleries, and layered game UI.
This document gives a quick overview; detailed APIs are in:
Quick example
import { RouterProvider, RootLayout, Layout, Page, useRouter } from "narraleaf-react";
function UI() {
return (
<RouterProvider>
<RootLayout>
{/* /home */}
<Layout name="home">
<Page name={null}> // default page /home
<Home />
</Page>
<Page name="detail"> // /home/detail
<Detail />
</Page>
</Layout>
{/* /about */}
<Layout name="about">
<Page name={null}>
<About />
</Page>
</Layout>
</RootLayout>
</RouterProvider>
);
}
function MenuButton() {
const router = useRouter();
return (
<button onClick={() => router.navigate("/about")}>Go to about</button>
);
}Path patterns (slugs & wildcards)
Layout and Page names support two special tokens:
- Slug / parameter –
:parammatches a single segment and exposes its value. - Wildcard –
*matches one segment (can be repeated).
// /user/:id/profile
<Layout name="user">
<Layout name=":id"> {/* matches any user id */}
<Page name="profile"> {/* e.g. /user/42/profile */}
<UserProfile />
</Page>
</Layout>
</Layout>Extract parameter values via the router helper:
const router = useRouter();
const params = router.extractParams(router.getCurrentPath(), "/user/:id/profile");
console.log(params.id); // "42"Wildcard example – match everything under /docs/**:
<Layout name="docs">
<Page name="*"> {/* /docs/installation, /docs/api/x, ... */}
<DocViewer />
</Page>
</Layout>Why the change?
- Nested layouts – Build complex UIs (side-bars, popups, tabs) by composing layouts.
- URL-like paths – Use familiar path patterns (
/user/:id/profile). - Better animation control – Exit/enter animations propagate through the layout tree.
Migration Guide
| Old API | New API | Notes |
|---|---|---|
<Page id="about" /> | <Page name="about" /> | id ➜ name |
router.push("about") | router.navigate("/about") | paths start with / |
| No concept | <Layout name="home">...</Layout> | new container |
Next steps
- Read the
LayoutRouterpublic methods. - Learn about
Layoutand path patterns. - Update your project: replace
Page idwithPage name, wrap related pages inLayouts, and userouter.navigate.
Component animations
LayoutRouter mounts and unmounts Page / Layout components automatically. To add enter/exit transitions, simply make the top-level node of your component a Framer Motion element. No extra router code needed.
import { motion } from "framer-motion";
export function PageContent() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
transition={{ duration: 0.3 }}
>
{/* your page UI */}
</motion.div>
);
}Wrap PageContent inside a <Page> and the transition will run whenever the route changes.