Live demo: Open /admin-tree A tree-defined admin section using createHierarchy(). Inherits breadcrumbs and permissions down the chain. (Requires admin — use the example header's Toggle 👤 button to switch users.)

What route inheritance gives you

When the routes in your app form a natural hierarchy (/documents/documents/:id/documents/:id/logs), the router can compute inherited metadata for each child instead of asking you to repeat it:

  • Breadcrumbs — concatenated parent → child
  • Permissions — child permissions are added to the parent's (cumulative AND, like filesystem ACLs)
  • Conditions — parent wrap({ conditions }) run before child conditions
  • Authorization callbacks — parent callbacks execute before child callbacks (fail-fast on first false)

Inheritance is opt-in. Without it enabled, routes stay independent and you define everything explicitly. Enable it once at app startup:

 

Which API should I use?

Two ways to declare the hierarchy. Both produce identical runtime behavior — they're just different ergonomics for writing route definitions.

Use…WhenLooks like
Flat definitions
createRoute() + setHierarchicalRoutesEnabled(true)
Mostly shallow routes (1–2 levels), or you need inheritX: false opt-outs on specific routes, or your route shape doesn't visually mirror your UI tree.One flat object with '/a', '/a/b', '/a/b/c' keys — child paths spelled out in full.
Tree definitions
createHierarchy()
Deep nesting (3+ levels) where every child inherits from its parent and the structure mirrors UI hierarchy. Most concise for admin-style apps.Single nested object with children: { ':id': { … } }. Child paths are relative — no repeated segments.

API 1 — Flat definitions with createRoute()

Standard flat-object route declarations. Inheritance triggers automatically when setHierarchicalRoutesEnabled(true) is set and the router can find a parent path by prefix match.

Breadcrumbs

 

Permissions (cumulative AND)

Permission checks chain like filesystem permissions — to reach a nested route, you must pass every ancestor check too.

 

Authorization callbacks chain too

 

Opting out of inheritance

The flat API supports per-route opt-out flags. The tree API doesn't — it always inherits. This is the main reason to choose flat over tree.

 

API 2 — Tree definitions with createHierarchy()

createHierarchy() takes a nested object and returns a flat routes object that <Router> can consume. Child paths are relative — the helper concatenates them to the parent path for you. Inheritance is always on inside the tree.

Side-by-side comparison

Flat
 
Tree
 

Path concatenation rules

  • Child paths join with /
  • Leading slashes in child paths are stripped ('logs' and '/logs' both produce the same result)
  • Parameters (:id) work in any position
  • Catch-all * can be used as a child key

Optional route names

Add name only on routes you'll navigate to with push(name, params). Unnamed children still get a path — they're just not in the named-routes registry.

 

Realistic example: admin app with permissions + authorization

 

Combining tree + flat

Both forms return plain route objects — spread them into the routes prop:

 

Implementation notes

Parent discovery

  • /documents/:id/logs → parent is /documents/:id
  • /documents/:id → parent is /documents
  • /documents → no parent (root)

The longest matching prefix wins. Computed once during route registration, not on every navigation.

Circular references

Built-in detection prevents infinite loops if routes somehow form a cycle. Mostly a non-issue with normal route definitions, but worth knowing it's caught.

See also

  • Permissions — role-based access control + reactive hasPermission()
  • Navigation guards — exit guards (separate concept from wrap({ conditions }) entry guards)
  • Route metadata — breadcrumbs, titles, routeContext()
  • Named routes — programmatic navigation with refactor-safe names