skip to content

In Angular's router, what does provideRouter(routes, withHashLocation()) change about the app's URLs, and when would you still choose it?

level: juniorimportance: should knowfreq 52%

answer

  1. which LocationStrategy is provided
  2. path strategy is the default
  3. server only sees the part before #
  4. deep-link reloads on static hosting

basics

~10 s

withHashLocation() replaces the default PathLocationStrategy with HashLocationStrategy, so routes live after a # (example.com/#/orders/42). The server only ever receives the root path, so deep-link reloads work on hosts that cannot rewrite URLs.

solid answer

~40 s

`withHashLocation()` is a `provideRouter` feature that provides `HashLocationStrategy` for the `LocationStrategy` token; without it Angular uses `PathLocationStrategy`, which writes real paths with the History API. With the hash strategy the address bar shows `/#/orders/42`: the route lives in the fragment, which the browser never sends to the server, so a reload always requests the root document and the app boots and routes client-side. That is the reason to pick it: static hosting or an embedded webview where you cannot configure a fallback to `index.html`. The costs are real, though: server-side rendering cannot see the route, URLs look dated, and switching strategies later breaks every saved link. In NgModule apps the same switch is `RouterModule.forRoot(routes, {useHash: true})`.

code

ts · 7 lines
ts
import {ApplicationConfig} from '@angular/core';
import {provideRouter, withHashLocation} from '@angular/router';
import {routes} from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes, withHashLocation())],
};

go deeper

for a junior

Know that the default is PathLocationStrategy with clean History API URLs, and that withHashLocation() moves the route after a # in the address bar.

for a middle

Explain why hash URLs survive a reload on any host: the fragment never reaches the server, so the root document always loads and the router reads the route client-side.

for a senior

Weigh the trade before choosing: hash URLs remove the server fallback but rule out server rendering of deep routes and make any later switch a link-breaking migration.

for a principal

Treat the URL shape as a public contract: pick the strategy with hosting, SSR plans and external links in mind, because changing it later breaks every saved and shared URL.

## What a LocationStrategy is Angular's router never touches `window.location` directly. It talks to the `Location` service from `@angular/common`, and `Location` delegates to an abstract **`LocationStrategy`** that decides how an internal router URL such as `/orders/42` is written to and read from the address bar. Two implementations ship with Angular: - **`PathLocationStrategy`** — the default. It is what `LocationStrategy` resolves to when nothing else is provided. It uses the History API (`pushState`/`replaceState`), so the address bar shows `https://example.com/orders/42`. Links are resolved against the `<base href>` element or the `APP_BASE_HREF` token. - **`HashLocationStrategy`** — puts the router URL after a `#`, so the same route shows as `https://example.com/#/orders/42`. `withHashLocation()` is simply a `provideRouter` feature whose only provider is `{provide: LocationStrategy, useClass: HashLocationStrategy}`. Nothing about route matching, guards or components changes; only the translation between router URL and browser URL does. ## Side by side | Aspect | `PathLocationStrategy` (default) | `HashLocationStrategy` (`withHashLocation()`) | |---|---|---| | Address bar | `/orders/42?tab=items` | `/#/orders/42?tab=items` | | What a reload sends to the server | the full path `/orders/42` | only `/` (the fragment stays in the browser) | | Server configuration needed | a fallback that serves `index.html` for unknown paths | none | | Server-side rendering | the server sees the route and can render it | the server cannot see the route | | A route fragment (`#section`) | `/docs#install` | a second hash: `/#/docs#install` | ## When the hash strategy is still the right call 1. **Static hosting you cannot configure.** Some file hosts, intranet shares and CDN buckets return a real 404 for `/orders/42` because no such file exists. The hash strategy sidesteps that entirely, because the server only ever serves the root document. 2. **Embedded or file-based shells.** An Angular app loaded from a `file:` URL or inside a native webview often has no server that could apply a rewrite rule. 3. **Legacy compatibility.** An app that has always used hash URLs has bookmarks and external links in that shape; switching strategies breaks them unless you add a redirect layer. ## What it costs - **No server rendering of deep routes.** Angular's SSR and prerendering render the URL the server receives; with hash URLs every request is `/`. - **Ugly and less shareable URLs**, and analytics or crawlers may treat everything after `#` as a same-page anchor. - **Fragments get awkward.** In-page anchors now live after a second `#`, which confuses people copying links and some third-party scripts. - **A migration later is a breaking change** for every saved link, so decide early. ## Configuring it With standalone bootstrapping, add the feature to `provideRouter`: ```ts import {bootstrapApplication} from '@angular/platform-browser'; import {provideRouter, withHashLocation} from '@angular/router'; import {App} from './app/app'; import {routes} from './app/app.routes'; bootstrapApplication(App, { providers: [provideRouter(routes, withHashLocation())], }); ``` In an NgModule application the equivalent is the `useHash: true` option of `RouterModule.forRoot()`. You can also provide `HashLocationStrategy` for `LocationStrategy` yourself, which is exactly what the feature function does. ## Under the hood It is tempting to think the hash strategy avoids the History API altogether. In Angular it does not: - **Writing** a URL: `HashLocationStrategy.pushState()` builds the external URL with `prepareExternalUrl()` (which prefixes `#`) and still calls the platform's `pushState`, so navigations create normal history entries (or replace one when `replaceUrl` is set). - **Reading** a URL: `path()` reads `location.hash` and strips the leading `#`, handing the router the same `/orders/42` it would get from the path strategy. - **Listening**: both strategies subscribe to `popstate` and `hashchange`, so Back, Forward and a hand-edited fragment all reach the router as navigations. - **Base href**: the hash strategy joins `APP_BASE_HREF`, if provided, inside the fragment; with the path strategy the base href prefixes the real path, which is why an app served from a sub-folder needs it set correctly. This is why switching strategies is a one-line change in the app: only the translation layer moves, while route recognition, guards, resolvers and `Router.events` behave identically. ## How to answer in an interview State the default first (`PathLocationStrategy`, History API URLs), then the mechanism of the alternative (the route sits in the fragment, which the browser does not send to the server), then the trade: hash URLs remove the need for a server fallback but give up server rendering and clean URLs. Current Angular guidance is to keep the default and configure the server fallback; reach for `withHashLocation()` only when you do not control the server.

  • Does switching to withHashLocation() change how routes are matched or how routerLink is written?
    No. Route tables, `routerLink` values, guards and `router.navigate()` calls stay exactly the same, because the router works with its own internal URL tree. Only the `LocationStrategy` changes, which translates that internal URL to `/#/...` in the address bar and back again when the user reloads or presses Back.
  • Why can't an Angular app with server-side rendering use hash URLs for its deep routes?
    The browser never sends the fragment to the server, so every request arrives as the root path. Angular's server renderer renders the URL it receives, so it can only ever render the root route; the real route is recognised later in the browser, which defeats the point of rendering on the server.

saying these in an interview costs you the question

  • Angular uses hash URLs by default
  • withHashLocation() changes how routes are matched
  • The server receives the part after # on reload
  • Hash URLs need a server rewrite rule to index.html
  • Switching strategies later keeps old bookmarks working