In Angular, how does a routerLink string differ from the array form, and how do '/', './' and '../' prefixes decide where the link points?
answer
- one command versus many
- dynamic values need separate slots
- leading slash starts at the root
- relative to the route that rendered it
basics
~20 sA routerLink string is one navigation command; the array form lists segments so IDs and matrix params slot in. A leading '/' starts at the root, no prefix or './' appends to the current route, '../' climbs a level.
solid answer
~40 s`routerLink` accepts a string, an array of commands or a `UrlTree`. A string is shorthand for a one-element array, so `routerLink="/products"` equals `[routerLink]="['/products']"`. The array form is what you use for dynamic parts: `['/products', product.id]` becomes `/products/42`, and an object after a segment becomes matrix params. The first command decides the starting point: a leading `/` resolves from the root, a bare name or `./` resolves under the route that rendered the link, and `../` goes up one route level. Query strings and fragments do **not** belong in the string: `?` and `=` get percent-encoded into the path, so you use the `[queryParams]` and `fragment` inputs instead. Binding `null` or `undefined` disables the link.
code
ts · 15 linesimport {Component, input} from '@angular/core';
import {RouterLink} from '@angular/router';
@Component({
selector: 'app-product-card',
imports: [RouterLink],
template: `
<a [routerLink]="['/products', productId()]">Details</a>
<a [routerLink]="['/products', productId(), 'reviews']" fragment="latest">Reviews</a>
<a routerLink="/products" [queryParams]="{category: 'books'}">All books</a>
`,
})
export class ProductCard {
readonly productId = input.required<number>();
}go deeper
Know both forms, that dynamic values go in array slots, and the meaning of a leading slash versus no slash versus ../ at the start of the first command.
Explain that the string is a path command, so query strings and fragments are encoded into the path, and that relative links resolve from the route that rendered the link.
Catch links that look right in a template but land on a not-found route, and prefer anchors with real hrefs so middle-click, copy-link and screen readers behave.
Push teams toward link helpers or route constants built on array commands so a renamed route breaks at compile or review time, not in production clicks.
## What `routerLink` accepts `RouterLink` is the directive from `@angular/router` that turns an element into a router-driven link. Its `routerLink` input takes one of four shapes: - **a string** such as `routerLink="/products"` - shorthand for an array holding that one string; - **an array of commands** such as `[routerLink]="['/products', id]"` - passed to `Router.createUrlTree()`; - **a `UrlTree`** - used as-is, with no further URL building; - **`null` or `undefined`** - effectively disables the link: no `href` is rendered and a click does nothing. On an `<a>` or `<area>` the directive writes a real `href`, so hovering, copying and opening in a new tab work. On any other element (a `<button>`, a `<div>`) there is no `href`; the directive handles the click and, when there is no `tabindex` attribute, adds `tabindex="0"`. ## String versus array The string form is convenient for static paths. Inside it, slashes still split segments, so `routerLink="/products/featured"` is two segments. The **array form** is needed as soon as a value is dynamic: | You write | Resulting URL (from the root) | |---|---| | `routerLink="/products"` | `/products` | | `[routerLink]="['/products', product.id]"` | `/products/42` | | `[routerLink]="['/products', {view: 'grid'}]"` | `/products;view=grid` (matrix params) | | `[routerLink]="['/products/featured', slug]"` | `/products/featured/spring` | String concatenation such as `routerLink="/products/{{ product.id }}"` also produces a URL, but the array keeps each value as its own segment and encodes it correctly, so a value containing `/` or spaces cannot split or break the path. ## How the first command picks the starting point The first command in the array decides where the new URL tree is built from: 1. **Leading `/`** - absolute: start from the root of the URL tree. 2. **No prefix or `./`** - relative: append to the route that rendered the link, so a link written inside a component routed at `products` resolves under `/products`. 3. **`../`** - go up one level of the route tree first, then apply the rest; `../../` goes up two. For `RouterLink`, "the current route" means the `ActivatedRoute` injected where the directive sits. For a link inside a routed component that is the route which rendered that component, not whatever child route happens to be showing further down, and for a link in the app shell outside every outlet it is the root route. The `relativeTo` input overrides the default. ## The query-string trap The string form is **a path command, not a URL**. The router encodes each segment, so writing a query string or fragment into it does not do what it looks like: ```html <!-- Wrong: renders href="/products%3Fpage%3D2" --> <a routerLink="/products?page=2">Page 2</a> <!-- Right: the path and the query travel separately --> <a routerLink="/products" [queryParams]="{page: 2}" fragment="top">Page 2</a> ``` The `?` and `=` become `%3F` and `%3D` inside a path segment, so the link points at a path no route matches. Query parameters go in `[queryParams]`, the fragment in `fragment`, and the handling of existing params in `queryParamsHandling`. ## Other inputs on the same directive - `queryParams`, `fragment`, `queryParamsHandling`, `preserveFragment` and `relativeTo` shape the URL tree the link builds. - `replaceUrl`, `skipLocationChange`, `state`, `info` and `browserUrl` are handed to `Router.navigateByUrl()` when the link is clicked. - When `routerLink` is a `UrlTree`, the URL-building inputs are not allowed; in dev mode the directive throws rather than silently ignore them. - Because it is a standalone directive, a standalone component must list `RouterLink` in its `imports`. ## Where each piece of a link comes from It helps to see one link as the sum of its inputs. For `<a [routerLink]="['../', product.id]" [queryParams]="{tab: 'specs'}" fragment="price">` rendered in a component routed at `products/featured`: 1. The first command `'../'` moves the starting point up from `featured` to `products`. 2. `product.id` is appended as one encoded segment, giving `/products/42`. 3. `queryParams` adds `?tab=specs`; with the default handling, any query already in the address bar is replaced, not kept. 4. `fragment` adds `#price`. The rendered `href` is `/products/42?tab=specs#price`, and that is exactly what the router navigates to on click. Inspecting the `href` in the browser's element panel is the fastest way to check a link before anyone clicks it. ## What an interviewer is checking A junior answer names both forms and the `/` rule. A stronger answer explains why dynamic values go in array slots, knows that relative links resolve from the route that rendered the link, and knows the query-string encoding trap, which produces links that look right in the template and land nowhere.
- Why is the array form safer than interpolating an ID into a routerLink string?Every array element after the first becomes one encoded segment, so a value with a slash, space or question mark stays inside its segment; only the first command is split on slashes. Interpolating into a string lets such a value split the path or be read as part of the command string, and the result only fails when someone clicks.
- What happens when routerLink is placed on a button instead of an anchor?The directive still navigates on click, but no href is rendered, so there is no hover preview, no open-in-new-tab and no link semantics for assistive technology. It adds tabindex 0 if the element has none. Use an anchor for real navigation.
- How do you disable an Angular routerLink conditionally?Bind it to null or undefined, for example [routerLink]="canOpen ? ['/orders', id] : null". The directive then renders no href and a click does not navigate.
saying these in an interview costs you the question
- Putting ?page=2 inside the routerLink string and expecting a query parameter
- Believing a link without a leading slash always resolves from the application root
- Building dynamic links by string concatenation instead of array segments
- Thinking routerLink on a button renders an href like an anchor does
- Claiming the string form and the array form produce different navigation behaviour