Ryz Labs/Interview questions/Angular developer
Interview questions

Angular interview questions for senior developers (2026)

Modern Angular questions for senior hires: standalone components, signals, zoneless change detection, new control flow, RxJS interop, DI, SSR and hydration.

These Angular interview questions are written for modern Angular, the version most teams are on or migrating to: standalone components, signals, zoneless change detection, built-in control flow with @if and @for, RxJS interop, dependency injection, and server-side rendering with hydration. They also cover the migration work many teams face, moving an NgModule and Zone.js codebase forward without stopping feature delivery. At Ryz, recruiters source senior Angular engineers from the applications they have built and maintained, candidates complete structured NTRVSTA AI interviews on topics like these, and recruiters review each one before and after. AI scores are advisory; people make the decisions.

How to use these questions

Angular changed more between versions 16 and 21 than in the five years before. A candidate who learned it in 2019 may still be strong, but should be able to explain signals and zoneless rendering, not just NgModules and ngOnChanges. Choose six questions: two fundamentals, two from change detection and rendering, two senior. For roles maintaining an older app, swap in migration questions.

Ask for the reasoning behind each choice ("why computed rather than effect here?"), and use the exercise to see real component design.

Fundamentals

What is a standalone component, and how is an app bootstrapped without NgModules?

A standalone component declares its own dependencies in imports and needs no NgModule. Standalone is the default in recent versions. The app starts with bootstrapApplication and an application config of providers.

export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection(),
provideRouter(routes, withComponentInputBinding()),
provideHttpClient(withFetch(), withInterceptors([authInterceptor])),
],
};

bootstrapApplication(AppComponent, appConfig);

What a strong answer shows: They know the provider-function style and can map old module concepts to it.

Explain signal, computed and effect. When is an effect the wrong tool?

A signal holds a value and notifies readers. computed derives a read-only value, lazily and memoized. effect runs side effects when signals it reads change. Using an effect to copy one signal into another is the common mistake: it creates extra change cycles and ordering bugs. Derive with computed, or linkedSignal when a derived value must also be writable.

readonly items = signal<CartItem[]>([]);
readonly total = computed(() =>
this.items().reduce((sum, i) => sum + i.price * i.qty, 0));

constructor() {
effect(() => localStorage.setItem('cart', JSON.stringify(this.items())));
}

What a strong answer shows: They derive state with computed and keep effects for syncing with the outside world.

How do signal inputs, outputs and model() work?

input() and input.required() return read-only signals, so derived values are just computed instead of ngOnChanges logic. output() replaces @Output with EventEmitter. model() creates a two-way bindable signal for [(value)] syntax.

export class RatingComponent {
readonly max = input(5);
readonly value = model.required<number>();
readonly stars = computed(() => Array.from({ length: this.max() }, (_, i) => i + 1));
}

What a strong answer shows: They replace lifecycle-hook plumbing with derived signals.

Why does @for require track, and what should you track by?

track tells Angular how to identify items so it can move existing DOM nodes instead of recreating them. Track a stable unique ID. Tracking by $index or the object reference causes full re-renders when data is refetched, losing focus and component state.

@if (orders().length) {
@for (order of orders(); track order.id) {
<app-order-row [order]="order" />
}
} @else {
<p>No orders yet.</p>
}

What a strong answer shows: They connect tracking to DOM reuse and state preservation.

A service provided in a component's providers array behaves differently than expected. Why?

Each component instance that lists the service in providers gets its own instance, shared only with its children. A service the team assumed was a singleton (providedIn: 'root') now has separate state per component. Hierarchical injectors are useful for per-component state but surprising when accidental.

What a strong answer shows: They can trace which injector resolves a token.

What is the difference between the constructor, ngOnInit and afterNextRender?

The constructor runs during injection; inputs are not set yet. ngOnInit runs after the first input values arrive. afterNextRender runs after the DOM renders and only in the browser, so it is the safe place for DOM measurement and browser-only libraries. With signal inputs, much ngOnInit logic becomes computed.

What a strong answer shows: They know which hook runs when, including on the server.

Why is calling a method in a template a performance problem, and what are the alternatives?

Template expressions run on every change detection pass for that view, so an expensive method runs repeatedly. Use a computed signal, which caches until its inputs change, or a pure pipe, which reruns only when its input reference changes.

What a strong answer shows: Understanding of how often templates evaluate and how memoization fixes it.

Intermediate

How do you set up lazy loading and route guards in a standalone app?

Use loadComponent for single pages and loadChildren pointing at a routes file for feature areas. Guards and resolvers are functions that use inject(). withComponentInputBinding() binds route params directly to component inputs.

export const routes: Routes = [
{
path: 'admin',
canMatch: [() => inject(AuthService).isAdmin()],
loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
},
{ path: 'orders/:id', loadComponent: () => import('./order-detail.component').then(m => m.OrderDetailComponent) },
];

What a strong answer shows: They know canMatch prevents the bundle from loading at all, unlike canActivate.

How do you refresh an expired token with a functional interceptor without firing duplicate refreshes?

Catch 401 responses in the interceptor, trigger one shared refresh request (a cached observable or a promise held in the auth service), and retry the original request with the new token once it resolves. Concurrent failures wait on the same refresh. If refresh fails, sign out.

What a strong answer shows: They handle concurrency and avoid refresh loops, including excluding the refresh call itself from the interceptor.

When do you keep RxJS, and how do you bridge it with signals?

RxJS remains the right tool for event streams over time: typeahead, websockets, retries, cancellation with switchMap. Signals are better for state the template reads. toSignal converts an observable for templates, toObservable goes the other way, and takeUntilDestroyed ends subscriptions with the component.

readonly query = signal('');
readonly results = toSignal(
toObservable(this.query).pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(q => this.api.search(q)),
),
{ initialValue: [] },
);

What a strong answer shows: They use each model for what it is good at instead of converting everything.

What do the resource APIs add for loading data with signals?

resource and httpResource fetch data reactively based on signals: when a request signal changes, the previous request is cancelled and a new one starts, and the result exposes value, loading and error states as signals. They are newer APIs, so check their stability status in your Angular version before building core flows on them.

What a strong answer shows: Awareness of current APIs and caution about experimental status.

What does a @defer block do, and when would you use one?

It splits the enclosed components into a separate chunk loaded on a trigger: on viewport, on interaction, on idle, or a condition, with optional prefetch. It suits heavy below-the-fold content like charts, comments or editors. The @placeholder and @loading blocks prevent layout shift.

What a strong answer shows: They reduce initial bundle size at the template level and think about layout shift.

How do you build typed reactive forms for a dynamic checkout?

Typed FormGroup and FormControl with nonNullable controls, FormArray for line items, custom and async validators, and cross-field validation at group level. Signal-based forms have appeared as an experimental API, so for production most teams still use reactive forms and bridge values with toSignal.

What a strong answer shows: Type-safe forms and a measured view of the newer forms API.

Change detection and rendering

How did Zone.js-based change detection work, and why did OnPush matter?

Zone.js patches async browser APIs and tells Angular after every event, timer or request that something might have changed, so Angular checks the component tree. With default strategy every component is checked. OnPush limits checks to components whose inputs changed by reference, that fired an event, or that were marked dirty, which saves a lot of work in large trees.

What a strong answer shows: A clear mental model of what triggers checks and what OnPush skips.

What does zoneless change detection change, and what breaks when you migrate?

With provideZonelessChangeDetection(), Angular no longer relies on Zone.js. Change detection is scheduled by signal updates read in templates, template event handlers, markForCheck (including via the async pipe) and a few other APIs. Code that mutates plain fields inside setTimeout, third-party callbacks or subscriptions without signals no longer updates the view. Migration means moving state to signals and removing Zone.js from polyfills.

What a strong answer shows: They know what schedules rendering now and how to find components that relied on zone magic.

A table with 5,000 rows is slow to update. How do you fix it?

Profile with Angular DevTools to see which components are checked and how long each takes. Fixes: OnPush or signal-driven components, track by ID, no method calls in templates, CDK virtual scrolling so only visible rows render, and pagination if the use case allows.

What a strong answer shows: They measure first and know that rendering fewer DOM nodes beats optimizing each one.

What causes ExpressionChangedAfterItHasBeenCheckedError?

In development mode, Angular checks bindings a second time and finds a value changed during the same pass, usually because a child or a lifecycle hook like ngAfterViewInit updated parent state. Fix the data flow rather than wrapping the update in setTimeout: compute the value earlier, derive it with computed, or move the state to a shared service.

What a strong answer shows: They treat the error as a data-flow design issue.

How do SSR and hydration work, and what breaks on the server?

The server renders HTML, and provideClientHydration() lets the client reuse that DOM instead of re-rendering. Event replay captures clicks before hydration finishes. Incremental hydration lets @defer blocks hydrate on triggers. On the server there is no window or localStorage, so browser-only code goes in afterNextRender. Direct DOM manipulation can cause hydration mismatches.

What a strong answer shows: They know the platform differences and hydration mismatch causes.

The production bundle grew 40% in a quarter. How do you find out why?

Build with stats or source maps and inspect with a bundle analyzer. Usual causes: a large library imported eagerly, a whole utility library instead of specific functions, or a route that lost lazy loading. Set budgets in angular.json so CI fails on regressions. The esbuild-based application builder is the default in recent versions and builds faster.

What a strong answer shows: Tooling plus a CI guardrail so it does not happen again.

Senior and architecture

How would you migrate a large NgModule and Zone.js app to modern Angular?

Upgrade versions one major at a time with ng update. Run the official schematics: standalone migration, control flow migration, signal input and query migrations, and inject() migration. Make components OnPush and signal-based feature by feature, then turn on zoneless once components no longer rely on Zone.js. Keep shipping features throughout.

What a strong answer shows: Use of automated migrations and an order that limits risk.

Signal services, NgRx Store or NgRx SignalStore: how do you choose state management?

Services with signals cover most apps. NgRx Store adds actions, reducers and effects with strong traceability and devtools, useful in large teams with complex flows. SignalStore offers a lighter, signal-native structure. Choose by team size and how much cross-feature state exists, and avoid putting every piece of state in a global store.

What a strong answer shows: A proportional answer, not a default to the heaviest option.

How would you structure an Angular monorepo for several teams?

Nx or a similar workspace with apps and libraries split by domain and type (feature, UI, data access, utilities), with lint rules enforcing allowed dependencies. Shared UI lives in a design system library. Affected-only builds and tests keep CI fast.

What a strong answer shows: Enforced boundaries and CI that scales with the codebase.

When are micro frontends worth it for Angular?

Only when teams need independent deployment and that need outweighs the cost: duplicated dependencies, version coordination, shared state and design consistency. Module Federation or Native Federation can load remote apps. A well-structured monorepo solves most problems that lead teams to consider micro frontends.

What a strong answer shows: Skepticism grounded in operating costs.

How do you test components in modern Angular?

TestBed with standalone components imported directly, component harnesses from the CDK for stable interaction with Material components, and HttpTestingController for HTTP. Zoneless tests should await fixture.whenStable() rather than rely on fakeAsync patterns tied to Zone.js. Karma is deprecated, and newer CLI versions support Vitest.

What a strong answer shows: Current tooling and tests that interact like a user.

Where can XSS still happen in an Angular app?

Angular sanitizes interpolation and property bindings by default. Risk comes from bypassSecurityTrustHtml on user content, direct DOM APIs like innerHTML through ElementRef, and server-rendered templates built from strings. Add a Content Security Policy, and use the built-in XSRF support with cookie-based auth.

What a strong answer shows: They know where the framework's protection stops.

How do you keep an Angular app current across major versions?

Angular ships a major roughly every six months with a defined support window. Upgrade one major at a time soon after release, run ng update migrations, fix deprecations as they appear, and track third-party library compatibility, which is often the real blocker.

What a strong answer shows: A routine cadence instead of multi-version leaps.

How do you improve Core Web Vitals on an Angular marketing or commerce site?

SSR or prerendering for fast first paint, NgOptimizedImage for sized, prioritized and lazy images, @defer for heavy widgets, incremental hydration to reduce main-thread work, and font loading that avoids layout shift. Measure with field data, not only Lighthouse.

What a strong answer shows: They tie Angular features to specific metrics.

How do you handle errors and observability in production?

Provide a custom ErrorHandler that reports to your monitoring tool with route and user context, handle HTTP errors centrally in interceptors with user-facing messages, and upload source maps privately so stack traces are readable. Track failed lazy chunk loads after deploys and prompt users to reload.

What a strong answer shows: Production experience, including stale chunk errors after deployments.

Red flags to watch for

A practical exercise

Give a 2 to 3-hour take-home: a standalone, zoneless Angular app with an orders list and detail view backed by a provided mock API. Requirements: search with debounce, filter state kept in the URL, a lazily loaded detail route, an @defer chart, and one component test. Ask for a short note on design decisions. Then hold a 30-minute review where they add a feature live.

Hire senior Angular developers vetted with these questions

To add engineers who already work this way, hire senior Angular developers through Ryz. They are the top 1% of the candidates we interview, they work on your team and in your repos, and they work within ±1h of US time zones, including on migrations from older Angular versions. Our vetting process shows how candidates are screened, and our Angular developer job description helps you scope the role.

FAQ

Should Angular interviews still cover NgModules and Zone.js?

Yes, if your app still uses them. Ask how they would migrate rather than how they would build new code that way. For new projects, focus on standalone components, signals and zoneless rendering.

How much RxJS should an Angular candidate know?

Mid-level engineers should use common operators and clean up subscriptions. Senior engineers should explain flattening operators (switchMap, concatMap, exhaustMap), error recovery and when signals are the simpler choice.

How do Angular interviews differ from general front-end interviews?

General front-end interviews focus on browser, CSS and JavaScript fundamentals. Angular interviews add framework architecture: dependency injection, change detection, routing and migrations. Senior Angular hires need both, so include one browser or accessibility question too.

Questions we didn't answer? Email info@ryzlabs.com.

Explore Ryz Labs

Staff augmentationDedicated development teamsAI pod teamsForward deployed engineersNearshore software developmentAI engineering teamsHire engineers by roleRyz Labs vs competitorsAlternatives guidesBuyer guidesCase studiesHow we vet engineers
Ryz Labs

Senior engineers in your time zone. AI pod teams that ship.

Tell us who you need. You'll get a scoped plan, a price and the names of the people who would do the work.

Start a conversation →