In Angular, how do you use an NgModule's components inside a standalone component, and a standalone component inside an NgModule?
answer
- both directions use imports
- modules give only their exports
- never declare a standalone class
- modules can re-export standalone
basics
~20 sBoth directions use imports: a standalone component lists the NgModule in its own imports and gets that module's exports; an NgModule lists the standalone class in its imports, and may re-export it, but never declares it (NG6008).
solid answer
~40 sA standalone component's `imports` array accepts NgModules, and it then sees exactly that module's `exports`, as any importer would. The reverse is also an import: an NgModule lists a standalone component, directive or pipe in its `imports`, and can add it to `exports` to share it. Putting a standalone class in `declarations` fails with `NG6008`, 'is standalone, and cannot be declared in an NgModule', and a standalone component cannot go in `bootstrap`. One trap: if a module imported by a routed or bootstrapped standalone component has providers, Angular collects them into a standalone injector for that component, which can create duplicate service instances.
code
ts · 21 linesimport { Component, NgModule, input } from '@angular/core';
import { CommonModule, CurrencyPipe } from '@angular/common';
import { LegacyTableModule } from './legacy/legacy-table.module';
import { OrderList } from './orders/order-list';
import { InvoiceRow } from './orders/invoice-row';
@Component({
selector: 'app-invoice',
imports: [LegacyTableModule, CurrencyPipe],
template: `<legacy-table [rows]="rows()" />`,
})
export class Invoice {
readonly rows = input.required<InvoiceRow[]>();
}
@NgModule({
declarations: [OrderList], // standalone: false
imports: [CommonModule, Invoice], // standalone classes are imported
exports: [Invoice],
})
export class OrdersModule {}go deeper
Remember that both directions go through imports: modules into a standalone component's imports, and standalone classes into a module's imports, never declarations.
Explain that importing a module gives only its exports, that modules can re-export imported standalone classes, and what NG6008 means.
Spot the provider trap: modules with providers imported by routed standalone components create standalone injectors and duplicate service instances.
Use the two interop directions to design a migration where converted leaf components are consumed by old modules and new features without a big-bang change.
## Two directions of interop Angular lets standalone components and NgModules coexist in one application, in both directions. That is what makes incremental migration possible: nothing forces a codebase to convert everything at once. Interviewers check that you know **which array** each direction uses, because the wrong choice is a compile error. | Direction | Where it goes | What you get | |---|---|---| | NgModule into a standalone component | the component's `imports` | the module's **exports** in that component's template | | Standalone class into an NgModule | the module's `imports` (never `declarations`) | the class usable in the module's declared templates | | Standalone class re-shared by a module | the module's `imports` **and** `exports` | the class visible to modules that import this module | ## An NgModule inside a standalone component A standalone component declares its template dependencies in its own `imports` array, and that array accepts NgModules as well as standalone classes: ```ts @Component({ selector: 'app-invoice', imports: [LegacyTableModule, CurrencyPipe], template: `<legacy-table [rows]="rows()" />`, }) export class Invoice { readonly rows = input.required<InvoiceRow[]>(); } ``` Rules to remember: - The component sees only the module's **exports**, exactly as another NgModule importing it would. Private declarations stay private. - Re-exports count: if `LegacyTableModule` re-exports another module, those exports come along. - If the imported module carries **providers**, they do not simply vanish. When the standalone component is created dynamically — bootstrapped, routed to, or created with `createComponent` — Angular collects providers from its import graph into a **standalone injector**, an environment injector created for that component. That can produce separate instances of services the module provides, which is why modules imported for their components should not carry singleton services. ## A standalone class inside an NgModule A module-based feature can use a standalone component, directive or pipe by **importing** it: ```ts @NgModule({ declarations: [OrderList], // standalone: false imports: [CommonModule, Invoice], // Invoice is standalone exports: [Invoice], // optional: share it with importers }) export class OrdersModule {} ``` - Putting a standalone class in `declarations` fails compilation with **`NG6008`**: "Component Invoice is standalone, and cannot be declared in an NgModule. Did you mean to import it instead?" - A module may **export** a standalone class it imports, so a shared module can keep handing the same building block to its importers while the component itself has become standalone. The official standalone migration produces exactly this shape. - A standalone component cannot be listed in an NgModule's `bootstrap` array (`NG6009`); a standalone root is started with `bootstrapApplication` instead. ## Why both directions matter for migration The two directions let a team convert **leaf components first**. A converted component keeps working inside the old modules (imported, and optionally re-exported), while new standalone features can pull in old modules for anything not yet converted. At no point does the app need a big-bang rewrite. A useful mental model: 1. Standalone classes are **imported everywhere**. 2. Declared classes are **declared once** and **exported** to be shared. 3. NgModules are an **import** unit for both worlds. ## Common mistakes - Declaring a standalone component "because it is used in this module". - Expecting a module's non-exported components to be usable after importing it into a standalone component. - Importing a module that provides singleton services into routed standalone components, and then seeing two instances. - Using `importProvidersFrom(SomeModule)` to get a module's **components** — that function collects providers only, and belongs in environment providers such as the application config. - Importing `CommonModule` into every new standalone component out of habit. Built-in control flow (`@if`, `@for`) needs no import, and a template that uses a pipe such as `date` can import just `DatePipe`. ## Checking your answer in code review When reviewing mixed code, read each array for its job: `declarations` should contain only `standalone: false` classes; every standalone class reaches a module through `imports`; a standalone component's `imports` may list modules, but only for their exports. If a shared module both provides services and is imported by routed standalone pages, move the services to `providedIn: 'root'` before it causes duplicate state.
- Why can a module's providers produce duplicate services when a routed standalone component imports that module?When a standalone component is created dynamically, for example by the router, Angular walks its import graph and, if any imported NgModule has providers, registers them in a standalone injector created for that component. Those registrations shadow root instances, so the component tree below gets its own copies. Services meant as singletons should use `providedIn: 'root'`.
- Can an NgModule export a standalone component it does not declare?Yes. A module may export anything it imports, including standalone components, directives and pipes. This lets an existing shared module keep offering a component to its importers after that component has been converted to standalone, which is how a migration avoids touching every consumer at once.
saying these in an interview costs you the question
- A standalone component used in a module must be added to declarations
- Importing an NgModule into a standalone component exposes all its declarations
- Standalone components and NgModules cannot be mixed in one application
- importProvidersFrom is how a standalone component uses a module's components
- An NgModule can only export components it declares itself