Standalone components remove the need for NgModules for most app composition, routing, and DI setup. NgModules still solve a few practical problems: packaging legacy (non-standalone) declarations, consuming module-based third-party libraries (forRoot/forChild), and providing a single import/export “bundle” boundary that some teams and libraries still rely on. The senior distinction is module compatibility versus module necessity.
Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
What problems do NgModules solve that standalone components don’t?Frontend interview answer
This Angular interview question tests whether you can explain Do you still need NgModules with standalone Angular components, connect it to production trade-offs, and handle common follow-up questions.
- Do you still need NgModules with standalone Angular components explanation without falling back to memorized docs wording
- Modules and Standalone reasoning, edge cases, and production failure modes
- How you would answer the most likely Angular interview follow-up
Overview
Standalone components shift Angular’s "template dependency scope" from @NgModule to the component itself (imports on the component). For modern apps, that removes most reasons to create feature/shared modules.
NgModules still matter mainly as a packaging + compatibility layer for legacy declarables and module-based libraries/config APIs.
Problem / need | NgModule solves it by... | Standalone status |
|---|---|---|
Using non-standalone directives/pipes/components (legacy code or older libs) | Declaring them once and exporting them via a module | Standalone cannot directly import non-standalone declarables; you import the NgModule that exports them. |
Module-style library configuration (forRoot/forChild patterns) | Returning | Standalone supports this mostly via |
Single “bundle import” for a large set of template dependencies | One | Standalone can mimic with a shared array/const, but there’s no first-class “export scope” container like NgModule exports. |
Gradual migration in an existing NgModule app | You can convert leaf components to standalone while keeping module boundaries intact | Standalone is great for incremental migration, but NgModules remain the glue during the transition. |
Team convention: explicit public API surface (exports) for a domain | Exports define what the rest of the app can use from that domain | Standalone relies on regular TS exports + per-component imports; you lose the explicit “exports list” mechanism. |
// legacy-shared.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LegacyHighlightDirective } from './legacy-highlight.directive';
import { LegacyDatePipe } from './legacy-date.pipe';
@NgModule({
declarations: [LegacyHighlightDirective, LegacyDatePipe],
imports: [CommonModule],
exports: [CommonModule, LegacyHighlightDirective, LegacyDatePipe]
})
export class LegacySharedModule {}
// standalone component consuming legacy declarations via NgModule
import { Component } from '@angular/core';
import { LegacySharedModule } from './legacy-shared.module';
@Component({
standalone: true,
selector: 'app-user-card',
imports: [LegacySharedModule],
template: `
<div legacyHighlight>
{{ today | legacyDate }}
</div>
`
})
export class UserCardComponent {
today = new Date();
}
If you’re fully standalone, do this instead | Why it’s the equivalent |
|---|---|
Create reusable import bundles as constants (not modules) | Gives you “one name” to import across many standalone components without an NgModule exports list. |
Prefer provider functions ( | Moves app configuration to bootstrap/route providers and reduces NgModule-only configuration patterns. |
// Greenfield standalone feature: no new feature NgModule required
import { Component } from '@angular/core';
import { NgIf } from '@angular/common';
import { ProductCardComponent } from './product-card.component';
@Component({
standalone: true,
selector: 'app-product-list-page',
imports: [NgIf, ProductCardComponent],
template: `
<app-product-card *ngIf="featured" [product]="featured"></app-product-card>
`
})
export class ProductListPage {
featured = { id: 1, name: 'Keyboard' };
}
// Legacy/module-shaped library API: still compatible, but only because the library forces it
import { bootstrapApplication, importProvidersFrom } from '@angular/core';
import { TranslateModule } from 'some-translate-lib';
bootstrapApplication(ProductListPage, {
providers: [
importProvidersFrom(TranslateModule.forRoot({ defaultLang: 'en' }))
]
});
// Module-based library config still shows up a lot
import { bootstrapApplication, importProvidersFrom } from '@angular/core';
import { AppComponent } from './app.component';
import { TranslateModule } from 'some-translate-lib';
bootstrapApplication(AppComponent, {
providers: [
importProvidersFrom(TranslateModule.forRoot({ defaultLang: 'en' }))
]
});
Situation | Senior choice | Why |
|---|---|---|
New app with standalone-ready libraries | Create no new NgModule | Direct component imports + provider functions already cover normal composition and DI setup. |
Legacy directives/pipes/components still need declarations/exports | Keep or bridge an NgModule | Standalone cannot directly import non-standalone declarables. |
A library still exposes | Use | That is library compatibility, not proof that your feature still needs its own NgModule. |
You want one reusable import bundle | Use TS constants/barrels first | Packaging convenience is not the same thing as a required Angular module boundary. |
Sharp distinction
Module compatibility means Angular can still interoperate with a module-shaped dependency or legacy declarable. Module necessity means your own new feature still needs a fresh NgModule. In modern greenfield apps, the first can remain true while the second is usually false.
Standalone components cover most of what NgModules were used for (composition, routing, DI setup). NgModules still “win” mainly for legacy/non-standalone declarations and module-shaped third-party APIs (especially forRoot/forChild). In a greenfield app with modern libs, you usually don’t need to write new NgModules.
Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.