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?
answer
- no component, no outlet
- grouping guards and providers
- layout without a URL segment
- children render one level up
basics
~20 sA 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.
solid answer
~40 sA **componentless** route, `{path: 'admin', canActivateChild: [...], children: [...]}`, renders nothing itself: the router skips it and places its children into the same outlet the parent would have used. It exists to attach shared configuration: `canActivate`/`canActivateChild` guards, `resolve`, `data`, route-level `providers`, `loadChildren`. An **empty-path parent with a component**, `{path: '', component: AppShell, children: [...]}`, is the opposite: it adds a visual layer, a shell with its own `<router-outlet>`, while contributing no URL segment, so `/inbox` renders `AppShell` with `Inbox` inside it. Combine them as needed: a componentless `path: ''` groups routes under one guard without changing URLs or layout. Pair any empty-path route that has no children with `pathMatch: 'full'`.
code
ts · 19 linesimport {Routes} from '@angular/router';
import {authGuard} from './auth';
import {AppLayout, Inbox, Login, PublicLayout, Sent} from './views';
export const routes: Routes = [
// Layout, no URL segment
{path: '', component: PublicLayout, children: [{path: 'login', component: Login}]},
{
path: '',
component: AppLayout,
canActivateChild: [authGuard],
children: [
{path: 'inbox', component: Inbox},
{path: 'sent', component: Sent},
],
},
// Componentless: shared guard and lazy group, children render in the root outlet
{path: 'admin', canActivate: [authGuard], loadChildren: () => import('./admin/routes')},
];go deeper
Recall that a parent route can have no component and still group children, and that path '' adds no URL segment.
Explain where children render in each case, what a componentless parent is for, and how two empty-path layout parents split public and signed-in pages.
Use componentless parents for guard, provider and lazy boundaries and empty-path shells for layout, avoiding dummy wrapper components.
Shape the route tree so URL structure, layout structure and security boundaries are each expressed once, without forcing them to align.
## Two ways a parent can be "invisible" Angular's router lets a parent route leave a trace in only one of two places: the **DOM** or the **URL**. The two patterns are easy to mix up in an interview: | Pattern | Has a component? | Adds a URL segment? | Children render in | |---|---|---|---| | Componentless parent | no | usually yes (`path: 'admin'`) | the outlet the parent would have used | | Empty-path parent with component | yes | no (`path: ''`) | the parent component's own `<router-outlet>` | | Componentless empty-path parent | no | no | the outlet above | ## Componentless parents: configuration without a view ```ts { path: 'admin', canActivateChild: [adminGuard], providers: [AuditLog], children: [ {path: 'users', component: AdminUsers}, {path: 'billing', component: AdminBilling}, ], } ``` `/admin/users` renders `AdminUsers` in the **root** outlet; there is no admin component and no extra `<router-outlet>`. The parent contributes: - a shared URL prefix (`admin`); - `canActivate` and `canActivateChild` guards that protect every child; - `resolve` and `data` available to the children; - `providers`, which create an environment injector for the whole subtree; - `loadChildren` for a lazily loaded group. In the router's activation code, a route with no component is recursed into using the **same outlet map**, which is exactly why its children end up in the outlet above. ## Empty-path parents: layout without a URL ```ts { path: '', component: MailShell, children: [ {path: 'inbox', component: Inbox}, {path: 'sent', component: Sent}, ], } ``` `/inbox` matches the empty parent, then `inbox` beneath it: `MailShell` renders in the root outlet and `Inbox` in `MailShell`'s outlet. The URL never mentions the shell. This is the standard way to give a group of top-level URLs a shared layout, for example signed-in pages with a sidebar and signed-out pages without one: 1. `{path: '', component: PublicLayout, children: [...login, signup...]}` 2. `{path: '', component: AppLayout, canActivate: [authGuard], children: [...inbox, sent...]}` The router tries them in order and picks the branch whose children match. ## Matching subtleties - An empty-path parent **with children** uses prefix matching and consumes nothing, so it lets the children try the URL. - An empty-path route **without children**, such as a redirect or a default page, normally needs `pathMatch: 'full'`; otherwise it matches every URL as a prefix. Path matching details belong with route definitions, but this pairing is where parents and matching meet. - A route without a component needs `children`, `loadChildren` or `redirectTo`; with none of them, the router's config validation rejects it as invalid. ## Parameters and data Before Angular 22, componentless parents were one of the two cases where children inherited the parent's params and data by default. Since v22 the router's default inheritance is `'always'`, so that distinction matters mainly when an app sets `paramsInheritanceStrategy: 'emptyOnly'` explicitly. ## A common refactor Older route tables often contain a `WrapperComponent` whose template is only `<router-outlet />`, added so a group of routes could share a guard. That wrapper adds a component instance, an extra outlet and an extra DOM node for nothing. Deleting its `component` line turns the parent componentless: the guard, data and providers still apply, the URLs are unchanged, and the children render one level up. The reverse refactor, adding a component to a componentless parent, is how a group later gains shared chrome. ## Choosing - Need a shared guard, resolver, provider scope or lazy boundary, but no shared chrome: **componentless**. - Need shared chrome, but the URL should not show it: **empty-path with component**. - Need both a URL prefix and chrome: an ordinary parent, `path: 'mail'` with a component.
- Where does a componentless parent's child component render?In the outlet the parent itself would have used. The router recurses through a route with no component using the same outlet map, so no extra router-outlet is needed or possible at that level.
- Why can two routes both have path '' at the top level?Each is an empty-path parent with children, and empty-path parents consume no segment. The router tries them in order and takes the first whose children match the remaining URL, so /login and /inbox can end up under different layouts.
saying these in an interview costs you the question
- Adding a router-outlet for a componentless parent's children
- Believing an empty-path parent adds a segment to the URL
- Using a dummy wrapper component only to attach a guard to a group
- Omitting pathMatch 'full' on an empty-path redirect without children
- Thinking a route can have no component, children, loadChildren or redirect