skip to content

In Angular route definitions, what do the title and data properties do, and which title wins when routes are nested?

level: middleimportance: should knowfreq 42%

answer

  1. the document title changes on navigation
  2. static string or ResolveFn
  3. deepest primary route wins
  4. untitled routes keep the old title
  5. data is static metadata

basics

~20 s

title sets the document title when the route activates, as a string or a ResolveFn; with nesting, the deepest title on the primary outlet path wins. data attaches static metadata, such as an analytics id.

solid answer

~40 s

`title` on a `Route` is either a string or a resolver (`ResolveFn<string>` or a class) that computes one, for example from a post's slug. After a successful navigation, the default `TitleStrategy` walks the primary outlet path from the root to the deepest route and uses the last title it finds, then sets it with the `Title` service. If no route on that path has a title, it leaves the document title unchanged, so the previous page's title can linger. `data` is an object of static values — breadcrumbs labels, analytics ids, feature flags — that components, guards or strategies read from the route snapshot. It is not for fetched data: that is what resolvers and route resources are for.

code

ts · 15 lines
ts
import { inject } from '@angular/core';
import { ResolveFn, Routes } from '@angular/router';
import { PostList } from './posts/post-list';
import { PostDetail } from './posts/post-detail';
import { PostHeadlines } from './posts/post-headlines';
import { NotFound } from './not-found';

const postTitle: ResolveFn<string> = (route) =>
  inject(PostHeadlines).headlineFor(route.paramMap.get('slug') ?? '');

export const routes: Routes = [
  { path: 'posts', component: PostList, title: 'All posts', data: { analyticsId: 'post-list' } },
  { path: 'posts/:slug', component: PostDetail, title: postTitle, data: { analyticsId: 'post-detail' } },
  { path: '**', component: NotFound, title: 'Page not found' },
];

go deeper

for a junior

Recall that title sets the browser tab title and data holds static values attached to a route.

for a middle

Explain string versus ResolveFn titles, the deepest-primary-route rule and what belongs in data versus a resolver.

for a senior

Catch lingering titles, give every leaf a meaningful title for accessibility, and keep data limited to static configuration.

for a principal

Set conventions for titles and route metadata across teams, including accessibility checks and analytics naming.

## `title`: page titles from the route table Document titles matter for accessibility — screen readers announce them after navigation — and for tabs, history and bookmarks. Angular's router can set them for you from the route table: ```ts { path: 'posts', component: PostList, title: 'All posts' } ``` The `title` property accepts: - a **string** for a fixed title; - a **`ResolveFn<string>`**, a function run like a data resolver, which can `inject()` services and return the title synchronously or asynchronously; - a class implementing `Resolve<string>` (the older, class-based form). A resolved title is computed as part of the navigation, like other resolvers, and is exposed on `ActivatedRoute.title` and on the route snapshot's `title`. ## Which title wins After the navigation completes, the router's **`TitleStrategy`** updates the document title. The default one, `DefaultTitleStrategy`: 1. starts at the root of the router state; 2. walks down the **primary outlet** children to the deepest active route; 3. remembers the last route along that path that has a title; 4. sets that value with the `Title` service from `@angular/platform-browser`. Consequences: | Configuration | Resulting document title | |---|---| | Parent `title: 'Blog'`, child `title: 'All posts'` | `All posts` (deepest wins) | | Parent `title: 'Blog'`, child without title | `Blog` | | No route on the path has a title | unchanged — whatever the previous page set | | Named outlet child with a title | ignored; only the primary outlet path is walked | The "unchanged" row is a common bug: navigating from a titled page to an untitled one keeps the old title. Give every leaf route a title, or a parent title that covers its section. Formatting every title (for example adding the site name) is done with a custom `TitleStrategy`, which belongs to router customisation. ## `data`: static route metadata `data` is an object of arbitrary key/value pairs fixed in the route table: ```ts { path: 'posts/:slug', component: PostDetail, data: { breadcrumb: 'Post', analyticsId: 'post-detail' } } ``` Typical uses: - labels for **breadcrumbs** built by walking the route tree; - **analytics** or telemetry identifiers; - flags read by a **guard** or strategy, such as required roles or a reuse hint; - layout hints read by a shell component. It is read from `ActivatedRoute.data` or the snapshot's `data`, and, when the router is configured for it, bound to component inputs. Values are static: they do not change per navigation. For per-navigation values, use a resolver (`resolve`) or a route-level resource. ## How `title` and `data` relate Internally the resolved title is stored in the route's data under a private symbol key, which is why it resolves alongside other resolved data. You do not read it from there; use `title` on the route or snapshot. ## Titles, accessibility and tests - A distinct title per page lets screen-reader users confirm that a navigation happened and where they landed; identical titles across pages remove that cue. - Put the most specific information first (the post headline before the site name) so it survives tab truncation; the ordering convention itself is a job for a custom strategy. - A route test can navigate to a URL and then read the `Title` service's `getTitle()` to assert the expected text, which catches lingering or missing titles before release. ## For the blog - `posts` gets `title: 'All posts'`. - `posts/:slug` gets a `ResolveFn<string>` that looks up the post's headline from the slug, so each article's tab shows its headline. - `**` gets `title: 'Page not found'`, so a bad URL does not keep the previous page's title. ## What interviewers listen for The two title forms, the deepest-primary-route rule, the lingering-title trap, and the distinction between static `data` and fetched data. Mentioning that titles are an accessibility requirement, not decoration, is a plus.

  • Why does a page sometimes keep the previous page's title after navigation?
    The default `TitleStrategy` only sets the document title when some route on the primary path has a title. If the new route and its ancestors have none, it leaves the title as it was. Give every leaf, or its section parent, a title.
  • Should route data hold the post fetched for /posts/:slug?
    No. `data` is static configuration fixed when the table is written. Data that depends on the URL and must be loaded belongs in a resolver or a route-level resource, which run per navigation.

saying these in an interview costs you the question

  • The top-level parent's title overrides its children's
  • A route without a title resets the tab to the index.html title
  • Route data is re-fetched on every navigation
  • title can only be a static string
  • Titles on named outlet routes set the document title