Angular interview questions
Reviewed by Mark Dickie · Last updated
5 questions on this page you can answer and see marked on the spot. Go to the first one
Angular routing is the built-in module that maps browser URLs to components and manages navigation between views in a single-page application.
For an Angular interview, expect questions on configuring routes, using route guards to control access, resolving data before navigation, lazy-loading feature modules, and handling query parameters versus matrix parameters. You should also understand the difference between RouterModule.forRoot and RouterModule.forChild, how the router outlet works, and when to use programmatic navigation with the Router service versus routerLink directives.
| Concept | What to Know |
|---|---|
| Route configuration | Path matching strategies (prefix vs full), redirects, and child routes |
| Route guards | CanActivate, CanActivateChild, CanDeactivate, and Resolve interfaces |
| Lazy loading | loadChildren with dynamic imports to split bundles per feature |
| Navigation | routerLink, Router.navigate(), and RouterLinkActive for styling active links |
| Router outlet | Named outlets for auxiliary routes and multiple simultaneous views |
What does an Angular routing interview test?
Interviewers typically probe whether you can wire up a route tree and explain how Angular decides which component to render for a given URL. They want to see that you know the guard lifecycle, when data resolution happens relative to activation, and how lazy-loaded modules interact with the root injector. Questions often escalate from basic path matching to edge cases like nested outlets, guard return types with observables, and preloading strategies.
How should you prepare for Angular routing questions?
- Review the route configuration object:
path,component,redirectTo,pathMatch,children, andloadChildren. - Write at least one guard of each type (
CanActivate,CanDeactivate,Resolve) so you can discuss the functional vs class-based approaches introduced in Angular 14+. - Practice lazy-loading a feature module and explain the network and bundle-size implications.
- Study preloading strategies (
NoPreloading,PreloadAllModules, and custom strategies) and when each makes sense. - Get comfortable with
ActivatedRoutesnapshot versus observable subscriptions for reading parameters.
Key facts
- Tarmac has 18 Angular interview questions on this topic, 10 of them on this page, at difficulty 1–5 of 5.
- Tarmac tracked 261 job postings asking for Angular in September 2026.
- Roles asking for Angular advertise a median base salary of £67,500, across 42 job postings as of September 2026.
- Tarmac last reviewed these Angular interview questions on 28 September 2026.
At a glance
| Questions | 10 shown · 18 in the bank |
|---|---|
| Difficulty | 1–5 of 5 |
| Formats | Multiple choice, Fill in the blank, Short answer, Multiple answer, True / false, Code output |
What you'll review
- lazy loading routes
- route configuration
- route resolvers
Practice questions
Try one before you open the answer. Pick an option and press Check; it's marked on the spot.
Our Angular app ships a lazy-loaded AdminModule. The compliance team needs the first navigation into /admin/** to run a license check before any AdminModule component renders. Which architecture meets this requirement?#
Options
Show answer
Guard the lazy-loaded admin routes with a CanActivateFn that calls the license API. The router runs the guard before activating the route, so navigation is blocked and no AdminModule component renders until the check succeeds, while the module stays lazy-loaded.
A route guard implemented as a CanActivateFn runs before the router activates the route. If it returns an Observable that resolves to false (or a UrlTree redirect), navigation is blocked and no AdminModule component is created until the license check passes, while the lazy chunk stays lazy for the rest of the app. Calling the API in the module constructor does not block anything, so components would still render. Loading the module eagerly defeats lazy loading and checks the license globally rather than on admin navigation. A spinner inside AdminComponent means the component has already rendered before the check finishes, which violates the requirement.
In Angular routing, the special wildcard path _____ is placed last in the Routes array so it acts as a catch-all for any URL that does not match an earlier route (commonly used to show a 'not found' page).#
Show answer
In Angular routing, the special wildcard path ****** is placed last in the Routes array so it acts as a catch-all for any URL that does not match an earlier route (commonly used to show a 'not found' page).
Angular's Router matches routes in array order, and the literal ** path matches any URL segment sequence. Listing it last guarantees that only URLs unmatched by earlier routes fall through to it. Placing it earlier would shadow every subsequent route.
In Angular's router configuration, which property of a Route object links its path to the component that should be rendered?#
Options
Show answer
The component property on an Angular Route links its path to the component to render. When the URL matches that path, the Router creates the component and places it inside the <router-outlet>.
The component property on a Route tells the Router which component to instantiate and insert into the <router-outlet> when the route's path matches the URL. redirectTo sends the user to a different path instead of rendering a component, loadChildren lazy-loads a set of child routes, and canActivate is a guard that decides whether navigation may proceed, so none of them names the component to render.
Complete the Angular route so that the admin path lazily loads AdminModule only when that path is first navigated to. AdminModule is a class exported from src/app/admin/admin.module.ts. Use the dynamic import() syntax. You may assume Routes is already imported from @angular/router.#
Show answer
Complete the Angular route so that the admin path lazily loads AdminModule only when that path is first navigated to. AdminModule is a class exported from src/app/admin/admin.module.ts. Use the dynamic import() syntax. You may assume Routes is already imported from @angular/router.
const routes: Routes = [
{
path: 'admin',
**loadChildren**: () => import('./admin/admin.module').then(m => m.**AdminModule**)
}
];
Blank loadChildren is the route property name. Blank AdminModule is the module class name referenced inside the .then() callback.
The route property that triggers lazy loading of a child module is loadChildren, which takes a function returning a dynamic import. The .then() callback receives the module namespace object and returns the named export AdminModule, which is the NgModule class to load. Angular's build tooling splits the imported file into a separate bundle that is fetched only when the admin route is first navigated to. The component property is a static reference and loadComponent lazy-loads a single standalone component, so neither fits a module-based lazy load.
In an Angular standalone-application or NgModule-based application, a lazy-loaded route uses the loadChildren (or loadComponent) property with a dynamic import() instead of the component property. Assuming PreloadAllModules is NOT used (default NoPreloading strategy), when does the browser fetch the lazy-loaded module or component bundle?#
Show answer
The browser fetches the lazy-loaded bundle the first time the router matches a navigation to that route's path, at which point the dynamic import() runs. It is not fetched at application boot, because with the default NoPreloading strategy the chunk is excluded from the initial bundle and is only downloaded on demand.
With the default NoPreloading strategy, Angular does not fetch lazy-loaded bundles at startup. The dynamic import() in loadChildren or loadComponent is only evaluated when the router matches a navigation to that route for the first time, which causes the browser to request the separate chunk on demand. Later navigations to the same route reuse the already-loaded chunk.
In an Angular (v17+) application using NgModule-based routing, the root AppModule imports RouterModule.forRoot(routes). A feature route is configured with loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule). FeatureModule does not call forRoot() or forChild(), but it declares FeatureComponent and lists FeatureService in its providers array. FeatureService is a class decorated with a bare @Injectable() (no providedIn metadata). Which statement correctly describes the behavior?#
Options
Show answer
In an Angular NgModule app, a bare @Injectable() service listed in a lazy-loaded module's providers array is available only to components in that feature module's subtree. Angular creates a child injector for the lazy module when its route first loads, so the service is not merged into the root injector and needs no RouterModule.forChild() call.
When Angular lazy-loads a module referenced by loadChildren, it creates a child injector for that module. Any provider in the lazy module's providers array, including a bare @Injectable() FeatureService with no providedIn, is registered in that child injector rather than the root injector, so only components in or below FeatureModule can inject it. The claim that it is merged app-wide is false because lazy providers are not merged into the root. The claim that RouterModule.forChild() is required is false because forChild() only registers route configuration, not DI providers. The tree-shaking claim is false because an explicit entry in a providers array keeps the class in the bundle; tree-shaking concerns providedIn: 'root' or 'any' services that nothing references.
Assume Angular 14+. Which of the following statements about route resolvers are true? Select all that apply.#
Options
Pick every one that applies.
Show answer
Angular's router takes the first value emitted by a resolver's Observable and does not require it to complete, and it activates a route only after all of that route's resolvers have emitted. Returning null from a resolver does not cancel navigation; the null is stored in ActivatedRoute.data. Resolvers run after route guards, not before them.
The router treats each resolver's return value as a data source it must wait on. For an Observable, it takes the first emitted value (similar to first() or take(1)) and tears down the subscription, so the stream does not need to complete; an infinite Observable works. When a route has several resolvers, they are invoked together and combined with forkJoin-style semantics, so they run in parallel and the route activates only once all of them have produced a value. Returning null is a valid resolved value: it is stored in ActivatedRoute.data and navigation proceeds normally. A plain synchronous value is also accepted, because the router wraps non-Observable results before using them. Resolvers run after the guards, not before, so the statement about guards reading resolved data is backwards.
Consider the following Angular standalone-app route configuration using provideRouter with withPreloading(PreloadAllStrategy). The adminGuard and reportsGuard both return false for non-admin users, and true for admins. After a non-admin user's initial navigation to /, which lazy route bundles does the RouterPreloader download via the PreloadAllStrategy?typescript// app.routes.tsexport const routes: Routes = [ { path: 'admin', canMatch: [adminGuard], loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES), }, { path: 'reports', canLoad: [reportsGuard], loadChildren: () => import('./reports/reports.routes').then(m => m.REPORTS_ROUTES), }, { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.routes').then(m => m.DASHBOARD_ROUTES), },];// app.config.tsprovideRouter(routes, withPreloading(PreloadAllStrategy))#
Show answer
The admin and dashboard bundles are preloaded; the reports bundle is not. The RouterPreloader evaluates canLoad guards before downloading a lazy route's code and skips routes where the guard returns false — so reports is excluded. canMatch guards, however, run after the route's module or component is already loaded; they only decide whether the route matches the current URL segment and do not gate the bundle download. Therefore the admin route is still preloaded even though adminGuard returns false for the non-admin user, the reports route is not preloaded, and the unguarded dashboard route is always preloaded. This is exactly why Angular 15+ deprecated canLoad in favor of canMatch: canMatch offers no 'code protection' — the bundle is always fetched — while canLoad prevents unauthorized users from downloading protected code at the cost of not being usable in loadComponent or per-route canMatch composition.
The admin and dashboard bundles are preloaded; the reports bundle is not. canLoad guards exist specifically to gate whether a lazy route's code bundle is downloaded. The RouterPreloader evaluates canLoad guards before preloading and skips routes where they return false — so reports (whose reportsGuard returns false for non-admins) is excluded from preloading. canMatch guards, by contrast, run after the route's module or component is already loaded; they only determine whether the route matches the current URL segment and do not gate the bundle download or preloading decision. Therefore, the admin route (guarded only by canMatch) is still preloaded even though adminGuard returns false, the unguarded dashboard route is always preloaded by PreloadAllStrategy, and only the reports route (guarded by canLoad returning false) is withheld. This distinction is the core reason Angular 15+ deprecated canLoad in favor of canMatch: canMatch provides no 'code protection' guarantee — the bundle is always fetched — whereas canLoad prevents unauthorized users from downloading protected code.
In an Angular 17+ standalone app, consider the route tree a → b → c, where a and b are componentless (they define children but no component) and c is a leaf route with a component. On a first navigation to /a/b/c, where none of these routes was previously active, and each of a, b, and c has a canActivate guard, the Router runs the canActivate guards of a and b as well as the guard of c. True or false?#
Options
Show answer
True. Angular's Router runs canActivate guards for every newly activated route in the matched path, including componentless parent routes like a and b. Componentless routes still appear in the activated route snapshot chain, so their guards fire when a child such as /a/b/c is navigated to.
The Router builds the activated route chain from the matched route tree, a → b → c, and that chain includes componentless routes, which exist only to group children. During activation, the Router checks canActivate for each route in the chain that is newly activated (not reused from a previous navigation with the same route config). Since none of a, b, or c was active before this navigation, all three guards run, so the statement is true. The presence or absence of a component affects only what is rendered in a router-outlet, not whether the guard is invoked.
A route resolver is registered for the counter route, and CounterComponent reads the resolved value in ngOnInit. The resolver's Observable never completes: it emits a new value every 100 ms forever. When the router finishes activating /counter, what single value does console.log print inside ngOnInit? Give the printed output only.#
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve, Routes } from '@angular/router';
import { Observable, timer } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CountResolver implements Resolve<number> {
resolve(route: ActivatedRouteSnapshot): Observable<number> {
// emits 0, 1, 2, ... every 100 ms, never completes
return timer(0, 100).pipe(map((n) => n + 1));
}
}
export const routes: Routes = [
{ path: 'counter', component: CounterComponent, resolve: { count: CountResolver } },
];
@Component({ selector: 'app-counter', template: '' })
export class CounterComponent implements OnInit {
constructor(private route: ActivatedRoute) {}
ngOnInit(): void {
console.log(this.route.snapshot.data['count']);
}
}Show answer
1
Angular's router takes only the first value emitted by a resolver's Observable (it applies first() internally) and then unsubscribes, so the never-completing stream does not block navigation. timer(0, 100) emits 0 at t = 0 ms, and the map((n) => n + 1) operator turns that into 1. The router stores 1 as the count data, so route.snapshot.data['count'] is 1 when ngOnInit runs, and the later emissions 2, 3, … are discarded.
Related interview questions
Job market
See angular salaries and hiring demand from live job postings.
The other 8 questions
This page shows 10 and marks what you pick. That's as far as a page can go. A free account opens the other 8 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.
Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan