Angular is used by 18.2% of developer teams. Master 30 interview questions: Signals, RxJS operators, NgRx, lifecycle hooks, SSR, and zoneless change detection.
Angular is used by 18.2% of professional developers worldwide and powers enterprise apps at Google, Microsoft, and Forbes. This guide covers 30 interview questions - from core concepts and lifecycle hooks to Angular Signals, standalone components, NgRx, and zoneless change detection - each with a concise answer and a code snippet.
Angular is used by 18.2% of professional developers worldwide, with 51,737 companies globally running it in production (Stack Overflow Developer Survey 2025; cmarix.com, 2026). The average Angular developer earns $131,594 per year in the US, with top earners reaching $201,779 (Glassdoor via cmarix.com, Dec 2025).
Angular job demand grew 10% from 2023 to 2024, with 172,693 front-end job vacancies mentioning Angular in that period (esparkinfo.com, 2026). Whether you're targeting your first Angular role or a senior position, interviewers probe deeper than syntax - they test your understanding of change detection, reactivity, and the v17+ features like Signals and standalone components.
Key Takeaways
Angular powers 18.2% of professional developer teams and 51,737 companies globally (Stack Overflow 2025)
Angular v17+ introduces Signals, standalone components, and server-side rendering as first-class CLI features - expect interview questions on all three
Senior Angular roles probe change detection strategy (Default vs OnPush vs Zoneless), NgRx, and RxJS mapping operators
The biggest knowledge gap across all competitors:
model()two-way binding,takeUntilDestroyed(), and zoneless change detection
If your JavaScript fundamentals need a refresh before Angular prep, our top JavaScript interview questions and answers 2026 covers all 50 core JS concepts interviewers ask at product companies.
Angular's component-driven architecture underpins every other concept in the framework. These five questions appear in almost every Angular interview - from junior roles at startups to senior positions at enterprises running Angular on millions of daily active users. With 4.65 million weekly NPM downloads as of Feb 2026 (npm registry via cmarix.com), Angular is clearly not slowing down.

Angular (v2+) is a TypeScript-based, component-driven framework built by Google for scalable single-page applications. AngularJS (v1.x) was JavaScript-based and used an MVC pattern. Key differences: Angular compiles AOT, uses TypeScript, has a superior DI system, and is actively maintained. AngularJS reached end-of-life in December 2021.
Interview tip: Interviewers asking this expect you to mention TypeScript, component architecture, and the EOL of AngularJS - not just "it's newer."
A component is the fundamental UI building block decorated with @Component. The three parts: a TypeScript class (business logic), an HTML template (view), and a stylesheet (presentation). Every Angular app has at least one root component (AppComponent). Components form a tree structure from root to leaf.
@Component({
selector: "app-user-card",
templateUrl: "./user-card.component.html",
styleUrls: ["./user-card.component.scss"],
})
export class UserCardComponent {
@Input() userName = "";
}A module groups related components, directives, pipes, and services. The root AppModule bootstraps the app; feature modules like AuthModule isolate domains and enable lazy loading. With standalone components (Angular v15+, stable), @NgModule is no longer mandatory - components now declare their own imports directly.
@Component({
standalone: true,
imports: [CommonModule, RouterModule],
selector: "app-dashboard",
templateUrl: "./dashboard.component.html",
})
export class DashboardComponent {}{{ expression }} - renders component data in the template[property]="value" - sets a DOM property from component data(event)="handler()" - listens to DOM events and calls component methods[(ngModel)]="field" - syncs component and view in both directions<!-- Interpolation -->
<h1>{{ title }}</h1>
<!-- Property binding -->
<img [src]="imageUrl" />
<!-- Event binding -->
<button (click)="save()">Save</button>
<!-- Two-way binding -->
<input [(ngModel)]="username" />DI is a design pattern where a class receives dependencies from an external provider. Angular's hierarchical injector tree resolves services decorated with @Injectable({ providedIn: 'root' }) as singletons. You can scope providers to a module or component for isolated instances, and use InjectionToken for non-class dependencies like config objects.
@Injectable({ providedIn: 'root' })
export class AuthService {
isLoggedIn = signal(false);
}
@Component({ ... })
export class NavComponent {
private auth = inject(AuthService); // functional injection
}Interview tip: The inject() function (v14+) is now preferred over constructor injection in new Angular code - know both patterns.
Lifecycle hooks give you precise control over component initialization, updates, and teardown. Two lifecycle questions appear in nearly every Angular interview: the full execution order (which trips up even mid-senior candidates) and the often-confused relationship between the TypeScript constructor and ngOnInit.
Eight hooks run in this sequence:
1. ngOnChanges() - when @Input properties change (before ngOnInit)
2. ngOnInit() - once after first ngOnChanges; ideal for HTTP calls
3. ngDoCheck() - every change detection run; for custom detection
4. ngAfterContentInit() - after projected content () initializes
5. ngAfterContentChecked() - after every check of projected content
6. ngAfterViewInit() - after component view and child views initialize
7. ngAfterViewChecked() - after every check of the view
8. ngOnDestroy() - before Angular destroys the component; for cleanup
The constructor is a TypeScript class feature Angular uses only for DI - injecting services via constructor parameters. ngOnInit runs after Angular sets @Input bindings. All initialization logic (API calls, reactive form setup) belongs in ngOnInit.
@Component({ ... })
export class ProductComponent implements OnInit {
@Input() productId = '';
product = signal<Product | null>(null);
constructor(private productSvc: ProductService) {}
// DI only
ngOnInit() {
this.productSvc.get(this.productId)
.subscribe(p => this.product.set(p)); // safe: @Input is set
}
}Interview tip: Accessing @Input() values in the constructor returns undefined - they're not set yet. This is the most common ngOnInit vs constructor gotcha in interviews.
Angular has three directive types, and interviewers often ask you to distinguish structural from attribute directives before asking you to build a custom one.
@Component is technically a directive*ngIf, *ngFor, *ngSwitch (new syntax: @if, @for, @switch)ngClass, ngStyle, or custom directives with @DirectiveInject TemplateRef and ViewContainerRef. Use an @Input setter to react to the bound value, calling createEmbeddedView() to render or clear() to remove the template - the same mechanism *ngIf uses internally.
@Directive({ selector: "[appHasRole]", standalone: true })
export class HasRoleDirective {
private tpl = inject(TemplateRef);
private vcr = inject(ViewContainerRef);
private auth = inject(AuthService);
@Input() set appHasRole(role: string) {
this.vcr.clear();
if (this.auth.hasRole(role)) {
this.vcr.createEmbeddedView(this.tpl);
}
}
}RxJS is Angular's reactive backbone. HttpClient, Router events, and form value changes all return Observables. Interviewers at mid-to-senior levels will probe your knowledge of higher-order mapping operators - this is where most candidates stumble.
| Observables | Promises | |
|---|---|---|
| Values emitted | Multiple over time | Single |
| Execution | Lazy (starts on .subscribe()) | Eager (starts immediately) |
| Cancellable | Yes (unsubscribe) | No |
| Operators | Yes (map, filter, retry, etc.) | Limited (.then, .catch) |
| Angular use | HttpClient, forms, Router events | Rarely (route resolvers) |
All four flatten an inner Observable from an outer emission:
switchMap: Cancels the previous inner Observable when a new one arrives - ideal for search-as-you-typemergeMap: Runs all inner Observables in parallel - ideal for independent parallel requestsconcatMap: Queues and processes sequentially - ideal for ordered write operationsexhaustMap: Ignores new outer emissions while the current inner Observable is active - ideal for login button clicks (prevents duplicate submissions)// switchMap - cancels previous search on each keystroke
this.searchInput.valueChanges
.pipe(
debounceTime(300),
switchMap((query) => this.api.search(query)),
)
.subscribe((results) => this.results.set(results));From the trenches: The four RxJS mapping operators are the single topic separating mid-level Angular developers from seniors in technical screens. Candidates who explain
exhaustMap's use case for preventing duplicate form submissions demonstrate production experience - not just tutorial knowledge.
shareReplay(n) makes a cold Observable hot by multicasting it to all subscribers and replaying the last n emissions to late subscribers. Use it to avoid duplicate HTTP requests when multiple components subscribe to the same source.
readonly config$ = this.http.get<AppConfig>('/api/config').pipe(
shareReplay({ bufferSize: 1, refCount: true })
);Interview tip: refCount: true auto-disconnects from the source when no subscribers remain - always prefer it over shareReplay(1) without refCount to prevent memory leaks.
Routing architecture is a common senior-level topic. Interviewers care both about lazy loading (bundle size optimization) and route guards (authentication and authorization patterns).
Lazy loading defers the download of a feature module's JS bundle until the user navigates to that route. Use loadChildren for module-based routing or loadComponent for standalone components. This reduces the initial bundle and improves Time to Interactive. Preloading strategies (PreloadAllModules, custom PreloadingStrategy) warm up lazy routes in the background after the initial load completes.
const routes: Routes = [
{
path: "dashboard",
loadComponent: () =>
import("./dashboard/dashboard.component").then(
(m) => m.DashboardComponent,
),
},
];Route guards control navigation access:
CanActivate - prevent access to a routeCanActivateChild - protect child routesCanDeactivate - warn before leaving (e.g., unsaved form)CanMatch - determine if a route definition can even be matched (replaces deprecated CanLoad)Resolve - pre-fetch data before activating a routeIn Angular v15+, guards are written as plain functions instead of injectable classes.
// Functional guard (v15+)
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
return auth.isLoggedIn() ? true : inject(Router).createUrlTree(["/login"]);
};Change detection questions are the most reliable differentiator between junior and senior Angular developers. Understanding when Angular checks a component - and how to control it - is essential for building performant applications at scale.
ChangeDetectionStrategy.Default): Angular checks the entire component tree on every async event (click, timer, HTTP). Simple but expensive for large trees.ChangeDetectionStrategy.OnPush): Angular checks the component only when: (1) an @Input reference changes, (2) an event originates from inside the component, (3) an Observable subscribed with async pipe emits, or (4) markForCheck() is called. Dramatically reduces checks in large trees.@Component({
selector: "app-item-list",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<li *ngFor="let item of items()">{{ item.name }}</li>`,
})
export class ItemListComponent {
@Input() items: Item[] = [];
}Interview tip: OnPush works best with immutable data patterns - always pass new object references rather than mutating existing ones.
Zone.js monkey-patches all async APIs (setTimeout, Promise, XHR, DOM events) to create an execution context that tracks async operations. Angular's NgZone triggers change detection after every async task. Use NgZone.runOutsideAngular() to run performance-intensive tasks - scroll listeners, canvas animations, third-party analytics - without triggering change detection on every tick.
Angular Signals (developer preview in v16, stable in v17) are the most significant addition to the framework since RxJS integration. Every senior Angular interview in 2026 will include at least one Signal question. Standalone components (stable in v15) are now the recommended architecture for all new projects.
Signals are a synchronous reactive primitive - a value container that notifies consumers when its value changes. Unlike Observables: signals always hold a current value (no subscribe needed), are always synchronous, and Angular's renderer reads them directly without Zone.js. Use signal() for writable state, computed() for derived values, and effect() for side effects.
// Signals example
const count = signal(0);
const doubled = computed(() => count() * 2);
effect(() => console.log("Count changed:", count())); // logs on every change
count.set(5); // count = 5, doubled = 10
count.update((n) => n + 1); // count = 6Standalone components declare standalone: true in @Component and import their own dependencies directly - no @NgModule required. This eliminates module boilerplate and makes each component self-contained. Bootstrap with bootstrapApplication() instead of platformBrowserDynamic().bootstrapModule().
// main.ts
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes), provideHttpClient(), provideAnimations()],
});model() (Angular v17.1+) is a signal-based two-way binding primitive. In the child, model declares it; the parent binds with [(value)]. It replaces the @Input() + @Output() valueChange = new EventEmitter() boilerplate with a single, type-safe declaration.
// Child component
@Component({ standalone: true, ... })
export class RatingComponent {
value = model<number>(0); // replaces @Input + @Output EventEmitter
}
// Parent template
<app-rating [(value)]="userRating" />Insight:
model()is the Angular feature most underrepresented in 2026 interviews. Interviewers at enterprise Angular teams now consider it a baseline expectation for any v17+ project - mentioning it unprompted signals real production experience, not just documentation reading.
effect() runs side effects whenever tracked signals inside it change - use it only when no reactive alternative exists (syncing with non-Angular libraries, imperative DOM, or logging). untracked() reads a signal's value inside an effect() without registering it as a dependency, preventing infinite loops when you need to read state without reacting to its changes.
Performance and SSR questions have become standard at senior interviews since Angular v17 made SSR a first-class CLI feature. These questions assess whether candidates understand bundle optimization, hydration, and rendering strategy trade-offs.
Ahead-of-Time (AOT) compiles TypeScript and HTML templates into optimized JavaScript at build time. Benefits: faster runtime rendering (no in-browser compile step), smaller bundles (compiler not shipped to client), earlier detection of template errors, and improved security (no eval of template HTML). JIT compiles in the browser at runtime and is only used during ng serve for faster dev rebuilds.
Angular SSR (the rebrand of Angular Universal, now @angular/ssr) renders the application on the server and sends pre-built HTML to the browser. Benefits: faster First Contentful Paint, improved Core Web Vitals, and better SEO. Angular v17 made SSR a first-class CLI option (ng new --ssr), introduced partial hydration support, and improved developer experience with built-in TransferState for avoiding duplicate HTTP requests between server and client.
Without trackBy, Angular re-renders every DOM node in a list when the array reference changes. trackBy provides a unique-identifier function so Angular reuses existing DOM nodes. The new @for control flow block (Angular v17+) requires a track expression by default - the framework enforces the optimization rather than leaving it optional.
<!-- Old *ngFor with optional trackBy -->
<li *ngFor="let item of items; trackBy: trackById">{{ item.name }}</li>
<!-- New @for - track is required -->
@for (item of items; track item.id) {
<li>{{ item.name }}</li>
}These questions separate strong mid-level candidates from engineers ready for senior and staff-level roles. NgRx, zoneless change detection, and memory leak prevention are the most frequently asked senior topics according to the AngularSpace and brecht.io interview resources.
NgRx implements the Redux pattern using RxJS - a single immutable Store, Actions describing events, pure Reducers transforming state, Selectors reading state slices, and Effects handling side effects (HTTP, localStorage). Use NgRx when: state is shared across many components and services, you need an audit trail of state transitions (dev tools, replay), or the team requires strict unidirectional data flow. For most applications, Signals + services are sufficient and simpler.
Zoneless removes the Zone.js dependency - Angular schedules change detection only when signals or markForCheck() explicitly notify it. Enable it with provideExperimentalZonelessChangeDetection() in providers and remove zone.js from polyfills in angular.json. Benefits: ~100KB smaller bundle, faster startup, native async/await support, and compatibility with micro-frontends using other frameworks. Requires all state updates to go through Signals or explicit markForCheck().
bootstrapApplication(AppComponent, {
providers: [
provideExperimentalZonelessChangeDetection(),
provideRouter(routes),
],
});Both configure DI at the component level. providers makes a service available to the component, all child components, and any projected content (). viewProviders restricts the service to the component's own view only - projected content cannot inject it. This distinction matters when building reusable component libraries where internal services shouldn't leak into slotted content.
Common leak sources: un-unsubscribed Observables and setInterval callbacks. Best practices:
async pipe in templates (auto-unsubscribes on component destroy)takeUntilDestroyed() (Angular v16+) with DestroyRef - more ergonomic than the takeUntil(destroy$) patterneffect() cleanup functions for signal-based effectsngOnDestroy@Component({ ... })
export class DataComponent {
private destroyRef = inject(DestroyRef);
ngOnInit() {
this.dataService.stream$.pipe(
takeUntilDestroyed(this.destroyRef) // auto-unsubscribes on destroy
).subscribe(data => this.data.set(data));
}
}In practice: Un-unsubscribed HTTP polling intervals are the most common memory leak in Angular codebases at scale. They accumulate silently across route navigations and can double a tab's memory footprint within minutes.
takeUntilDestroyed()eliminates the entire class of bug without any boilerplate.
Interceptors implement HttpInterceptorFn (functional API, Angular v15+) and intercept every HttpRequest before it's sent and every HttpResponse before it reaches the caller. Common uses: attaching auth tokens, global error handling (redirect on 401), request/response logging, retrying failed requests, and showing loading indicators.
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getToken();
const authed = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
return next(authed);
};| Template-Driven | Reactive | |
|---|---|---|
| Definition | HTML-first, logic in template | Code-first, logic in component class |
| Validation | Directive-based (required, minlength) | Validator functions (Validators.required) |
| Testing | Harder - requires DOM | Easier - pure TypeScript |
| Scalability | Simple login/search forms | Multi-step wizards, dynamic field arrays |
| Async validators | Supported | Supported, easier to compose |
Interview tip: Reactive forms are the default recommendation for any form with dynamic fields, cross-field validation, or unit testability requirements. Template-driven forms are fine for a single-field search box - not for a multi-step checkout.
@defer (Angular v17+) is a native deferred loading primitive that lazy-loads a component template block based on a declarative trigger - no routing or manual loadComponent required. Triggers include on viewport (enters viewport), on interaction (click/focus), on idle, and when condition. The @placeholder and @loading sub-blocks handle fallback UI. Unlike route-level lazy loading, @defer operates at the component template level, making it ideal for below-the-fold heavy components like charts, rich text editors, and image galleries.
@defer (on viewport) {
<app-analytics-chart />
} @placeholder {
<div class="chart-skeleton" style="height: 300px;"></div>
} @loading (minimum 300ms) {
<app-spinner />
}Interview tip: @defer is Angular's answer to React's React.lazy() + . Knowing it in 2026 signals you've kept pace with v17+ - most candidates stop at route-level lazy loading.
If you're strengthening your JavaScript fundamentals alongside Angular prep, the top JavaScript interview questions guide at stackinterview.dev covers all 50 core JS questions interviewers ask. For hands-on practice, the JavaScript coding interview questions guide walks through real coding problems with optimized solutions.
For junior roles, focus on 15-20 core questions: components, data binding, directives, lifecycle hooks, services, and basic RxJS (Observable vs Promise). Angular is used by 18.2% of professional developers (Stack Overflow 2025), so interviewers expect solid fundamentals before probing advanced topics.
NgRx is expected at senior level (4+ years) and at enterprise companies. For junior-to-mid roles, knowing when NgRx is appropriate - and that Signals + services cover most use cases - is sufficient. Angular job demand grew 10% from 2023 to 2024 (esparkinfo.com), and most of those roles don't require NgRx on day one.
Reference Angular v17+ features (Signals stable, @for block, SSR as CLI option) and mention v15 standalone components as the current recommended architecture. Interviewers at companies using Angular v12-v14 still ask about @NgModule - verify the company's tech stack on their job description before the interview.
Yes. Angular has 51,737 companies in production (6sense via cmarix.com, 2026), 4.65M weekly NPM downloads, and a $131,594 average US developer salary (Glassdoor via cmarix.com, 2025). Its enterprise-grade tooling, opinionated structure, and Google backing make it the default choice for large-scale SPA development.
Angular interviews probe framework-specific concepts deeply (change detection, DI hierarchy, Zone.js, RxJS operators) because the framework is opinionated. React interviews focus more on patterns (state management, hooks, rendering behavior) since React itself is a library. Angular developers earn slightly more on average - $131,594 vs approximately $120,000 for React developers at similar experience levels.
Angular's evolution from @NgModule-based architecture to standalone components, Signals, and zoneless change detection in v17 means 2026 interviews test both the foundational concepts that haven't changed - DI, lifecycle hooks, change detection - and the modern APIs that separate candidates who've kept pace. The 28 questions in this guide cover what junior roles require through what senior-level positions demand.
The highest-signal answers in Angular interviews aren't definitions - they're trade-off discussions. Explaining when to use OnPush vs Default, when NgRx is overkill, and why exhaustMap prevents duplicate submissions shows production-level thinking, which is what top companies actually hire for.
Key Takeaways
Angular is used by 18.2% of professional developers, with 51,737 companies globally and average US salaries of $131,594
Signals (v17+), standalone components (v15+), and zoneless change detection are the modern Angular features every 2026 interview expects
The four RxJS mapping operators (switchMap, mergeMap, concatMap, exhaustMap) are the most reliable senior-level differentiator
takeUntilDestroyed(),model(), andCanMatchare the highest-signal answers candidates rarely give
Pair this guide with our complete JavaScript interview questions 2026 - TypeScript is a superset of JS, and every Angular interview eventually tests core JavaScript knowledge too.