skip to content

Nested & Named Outlets

Child routes rendered into a nested router-outlet, componentless and empty-path parents, and named auxiliary outlets with their URL syntax. Interviewers probe which views survive a child navigation.

part ofAngularoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In Angular, how do you configure child routes so a mail shell renders inbox, sent and message views in its own nested router-outlet?

level: juniorimportance: must knowfreq 62%

answer

  1. children array on the parent
  2. a second outlet in the parent
  3. child paths are relative
  4. inserted after the outlet

basics

~20 s

Give the parent route a component and a children array, and put a <router-outlet> in that component's template. Child paths are relative to the parent, and the child component is rendered as a sibling right after the nested outlet element.

solid answer

~40 s

The parent route `mail` gets `component: MailShell` and `children: [...]` with entries such as `inbox`, `sent` and `:id`; their paths are relative, so `inbox` matches `/mail/inbox`. `MailShell` imports `RouterOutlet` and places `<router-outlet />` where the child view belongs, while the app root's own outlet renders `MailShell` itself. A child with `path: ''` (a component, or `redirectTo: 'inbox'` with `pathMatch: 'full'`) handles bare `/mail`. Moving between children keeps the `MailShell` instance and swaps only the nested view, so the folder list and its state survive. One DOM detail trips people up: the router inserts the routed component **after** the `<router-outlet>` element as a sibling, not inside it, so CSS like `router-outlet > *` never matches.

code

ts · 15 lines
ts
import {Routes} from '@angular/router';
import {Inbox, MailShell, MessageView, Sent} from './mail';

export const routes: Routes = [
  {
    path: 'mail',
    component: MailShell,
    children: [
      {path: '', redirectTo: 'inbox', pathMatch: 'full'},
      {path: 'inbox', component: Inbox},
      {path: 'sent', component: Sent},
      {path: ':id', component: MessageView},
    ],
  },
];

go deeper

for a junior

Know the children array, the nested router-outlet in the parent component, and that child paths are written relative to the parent.

for a middle

Explain the empty-path default child, match order among children, sibling insertion after the outlet, and why the parent survives child navigation.

for a senior

Design shells so shared state lives in the parent, and diagnose blank child areas by checking whether the nested outlet exists yet.

for a principal

Decide how many layout shells a product needs and which state each owns, keeping route nesting aligned with how screens really share chrome.

## Two levels of outlets Angular's router renders each matched route's component into a `<router-outlet>`. The app root's template has the first outlet; a parent route whose component has its **own** `<router-outlet>` hosts its children there. That is all a nested layout is: a route tree mirrored by a tree of outlets. ```ts export const routes: Routes = [ { path: 'mail', component: MailShell, // rendered in the root outlet children: [ // rendered in MailShell's outlet {path: '', redirectTo: 'inbox', pathMatch: 'full'}, {path: 'inbox', component: Inbox}, {path: 'sent', component: Sent}, {path: ':id', component: MessageView}, ], }, ]; ``` ```ts @Component({ selector: 'app-mail-shell', imports: [RouterOutlet, RouterLink], template: ` <nav><a routerLink="inbox">Inbox</a> <a routerLink="sent">Sent</a></nav> <main><router-outlet /></main> `, }) export class MailShell {} ``` ## How the URL maps | URL | Root outlet | MailShell's outlet | |---|---|---| | `/mail` | `MailShell` | redirected to `/mail/inbox` | | `/mail/inbox` | `MailShell` | `Inbox` | | `/mail/sent` | `MailShell` | `Sent` | | `/mail/42` | `MailShell` | `MessageView` | Key rules: - **Child paths are relative** to the parent: `inbox`, not `mail/inbox`. - **Order still matters** within `children`: static `inbox` and `sent` sit before `:id` so they are not captured as ids. - The parent component must list `RouterOutlet` in its `imports` (it is a standalone directive), and `RouterLink` for its links. - Relative links inside `MailShell`, such as `routerLink="sent"`, resolve under `/mail`. ## What survives a child navigation Going from `/mail/inbox` to `/mail/sent` changes only the child route. The router reuses the `MailShell` instance, because its route config is unchanged, and swaps the component in the nested outlet: 1. `Inbox` is destroyed; the nested outlet emits `deactivate` with the old instance. 2. `Sent` is created; the outlet emits `activate` with the new instance. 3. `MailShell` and anything in it, such as the folder list, its scroll position and its loaded counts, stay untouched. That is the practical payoff of nesting: shared chrome loads once per section. ## Where the component lands in the DOM `<router-outlet>` is a directive on an element that stays in the DOM as an **anchor**. The routed component is inserted through the outlet's `ViewContainerRef`, which places it **after** the anchor, as a sibling: ```html <main> <router-outlet></router-outlet> <app-inbox>...</app-inbox> </main> ``` Consequences: - `router-outlet > *` or `router-outlet app-inbox` selectors never match; - a flex or grid container sees two children: the empty outlet element and the routed component. Style the routed component's `:host`, or the container, rather than the outlet. ## Routes that match with no outlet If a child route matches but the parent's template has no `<router-outlet>` yet, for example because it sits inside an `@if` that is still false, the router does not fail. It keeps the activation context and renders the child as soon as an outlet with that name is created. A missing outlet therefore shows up as a blank area, not an error, which is worth checking first when a child view silently does not appear. ## Outlet events at the nested level The nested outlet is a full `RouterOutlet`, so `MailShell` can listen to it: `<router-outlet (activate)="onView($event)" />` receives each newly created child component, and `(deactivate)` the one being destroyed. That is a clean place to move focus to the new view's heading or to update a toolbar, without the child knowing about the shell. Because the shell itself is reused across child navigations, these events are the shell's signal that its content changed. ## What interviewers look for - `children` plus a nested `<router-outlet>` in the parent component. - Relative child paths and an empty-path default child. - Awareness that the parent survives child navigation. - Bonus: the sibling insertion detail and its CSS consequences.

  • Why do CSS rules like router-outlet > * not style the routed component?
    The routed component is inserted after the router-outlet element as a sibling, through the outlet's ViewContainerRef. The outlet element stays empty as an anchor. Style the routed component's :host or the surrounding container instead.
  • What happens if a child route matches but the parent has no router-outlet?
    Nothing renders for the child and no error is thrown. The router keeps the activation context, and if an outlet appears later, for example when an @if becomes true, the child component is created then.
  • Does the parent component get recreated when moving between its child routes?
    No. The parent's route config is unchanged, so the default reuse keeps the parent instance and only the component in the nested outlet is destroyed and replaced.

saying these in an interview costs you the question

  • Repeating the parent path inside child paths, like mail/inbox under mail
  • Expecting the routed component to render inside the router-outlet element
  • Believing the parent component is destroyed on every child navigation
  • Putting :id before static child paths such as inbox
  • Forgetting RouterOutlet in the parent standalone component's imports
open as a page

In Angular's router, how does a componentless parent route differ from an empty-path parent route with a component, and when do you use each?

level: middleimportance: should knowfreq 45%

basics

~20 s

A componentless parent has children but no component, so its children render in the outlet above it; it groups guards, resolvers, providers or lazy children. An empty-path parent with a component adds a layout shell without a URL segment.

open as a page

In Angular, how do you open a compose panel in a named router-outlet beside the inbox, and what does the resulting URL look like?

level: middleimportance: should knowfreq 35%

basics

~10 s

Add <router-outlet name="side" />, give a route outlet: 'side', and navigate with an outlets command such as [{outlets: {side: ['compose']}}]. The URL becomes /inbox(side:compose); setting side to null closes the panel.

open as a page

In Angular, what do a router-outlet's activate and deactivate outputs emit, and when would you pass routerOutletData instead?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

activate emits the routed component instance when the outlet creates it, and deactivate emits it when the outlet destroys it. To send context into routed components, bind routerOutletData and inject ROUTER_OUTLET_DATA as a signal instead of poking the instance.

open as a page