Ramonda

Nested outlets

A routed page can have routes of its own — a settings screen with Profile, Billing and Team tabs. Put a second <RouteOutlet> inside the page:

const settingsRoutes = createRoutes({
  "/settings": <Profile />,
  "/settings/billing": <Billing />,
  "/settings/team": <Team />,
});

@Host("section")
class Settings extends Component {
  render() {
    return (
      <div className="settings">
        <SettingsNav />
        <RouteOutlet routes={settingsRoutes} />
      </div>
    );
  }
}

const routes = createRoutes({
  "/": <Home />,
  "/settings": <Settings />,
  "/settings/billing": <Settings />,
  "/settings/team": <Settings />,
  "*": <NotFound />,
});

The outer table names every path that should render Settings, because matching is on the whole pathname, not segment by segment. The upside: the route table stays a flat list you can read top to bottom.

What stays put across navigation

Moving between /settings/billing and /settings/team re-renders only the inner outlet. Settings and its SettingsNav are the same instances — same state, same scroll position, no remount — because the outer match didn't change. It is the nav-bar property, one level down.

Params at each level

Each outlet publishes the params it matched, and a component reads the nearest outlet above it:

const { teamId } = this.route.params("/teams/:teamId");

Name the pattern of the outlet that matched this component — not an outer one. Each outlet publishes the params it matched, so an inner page reads the inner pattern, and naming a param the inner route does not supply throws rather than handing back undefined.

Next