Home › Frontend › Standalone Component Imports: Angular Fix
Beginner 5 min · September 23, 2026

Standalone Component Imports: Angular Fix

Add the missing piece to the imports array.

N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Everything here is grounded in real deployments.

Follow
✓ Production
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
Before you start⏱ 9 min
  • ✓Angular components and modules
  • ✓Basic routing with routerLink
  • ✓An Angular CLI project to edit
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • Standalone components have no NgModule, so every child component, directive, pipe, and helper module must sit in their own imports array
  • NgModule apps share dependencies per module; standalone apps declare them per component, and children never inherit imports
  • Router directives need RouterModule imports and ngIf/ngFor need CommonModule in each using component
  • Migrate leaf-first with production AOT builds, since mocked specs never compile the real imports arrays
✦ Definition~90s read
What is Angular Standalone Component Not Found in Imports?

Standalone components, stable since Angular 15 and the default for new code, are components that declare standalone: true and list their template dependencies in an imports array instead of belonging to an NgModule. That array accepts other standalone components, directives, pipes, and NgModules such as CommonModule, FormsModule, or RouterModule.

★
Think of NgModule apps as office kitchens stocked per floor: one shared pantry serves every team on that floor.

The compiler builds each template's scope exclusively from these entries: a selector resolves if and only if its owner appears in scope. No global registry, no transitive inheritance, no ambient availability.

NgModules organized the same dependencies per feature: declarations listed the owned components, imports brought shared capabilities, and exports shared them onward. Lazy modules added sealed scopes with their own injectors. The standalone model flattens this: each component owns its scope directly, and applications bootstrap via bootstrapApplication with providers from app.config.ts instead of a root module.

ROUTER_DIRECTIVES-style import lists from the earliest Angular eras were the prototype; modern imports arrays are their mature form, typed and tree-shakable.

The two systems interoperate through precise rules. NgModules import standalone components via their imports array. Standalone components import NgModules the same way, gaining all their exports. Providers cross through importProvidersFrom or functional providers like provideHttpClient().

Knowing which direction each crossing takes, and verifying with AOT builds, turns migration from a gamble into a checklist.

Plain-English First

Think of NgModule apps as office kitchens stocked per floor: one shared pantry serves every team on that floor. Standalone components are lunch boxes: each person packs exactly what they'll eat. Converting to standalone without packing the imports is showing up with an empty box and wondering where lunch went. The fix is packing deliberately: every selector your template uses goes in your imports box, and shared snacks like router links need packing too.

You convert a component to standalone, save, and the template fills with red: 'app-avatar' is not a known element. The component exists. It worked yesterday. The only change is the standalone flag, and suddenly Angular can't see a file sitting right next to it.

Standalone components carry no NgModule, so nothing supplies their template dependencies implicitly. Every child component, directive, pipe, and helper module must appear in the component's own imports array. Yesterday AppModule provided AvatarComponent to the whole feature; today nobody provides it to your template. The compiler is right: from its scope, that selector doesn't exist.

Migration multiplies the confusion. Half the codebase still lives in modules while the other half imports directly, and each dependency demands the wiring style its author chose. Router directives, CommonModule structural directives, and third-party UI pieces each fail in their own dialect of the same missing-import error.

This guide untangles it. You'll learn the standalone imports mental model, how it differs from NgModule wiring, which entries router and structural directives need, how to mix both eras safely, and how to migrate feature by feature without breaking half your routes.

The Standalone Imports Mental Model

A standalone component's imports array is the complete list of what its template may use. Child components, attribute directives, structural directives via their modules, and pipes all enter scope exclusively through this array. Nothing is inherited from parents, nothing leaks sideways from siblings, and nothing arrives globally. When the compiler reports an unknown element, it means exactly what it says: within this template's scope, that selector was never registered.

This explicitness replaces the old transitive visibility where importing a module granted its entire export surface to every template in the feature. The new model trades that convenience for precision: templates declare their true dependencies, the bundler drops everything else, and readers see the full dependency list without chasing module files. The cost is repetition: a component used in ten templates appears in ten imports arrays, which feels verbose until you realize each entry is documentation.

Build the verification habit mechanically. For every selector in the template, find its owner and confirm the import entry. Automate the check with lint rules that flag unknown selectors at PR time, and keep a snippet that scaffolds new components with the common imports pre-listed. Developers who map selectors to imports as they type never meet this error; developers who add imports after the fact meet it weekly.

src/app/header.component.tsTYPESCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component } from '@angular/core';
import { AvatarComponent } from './avatar.component';
import { BadgeComponent } from './badge.component';

@Component({
  selector: 'app-header',
  standalone: true,
  // Every selector used below must appear here.
  imports: [AvatarComponent, BadgeComponent],
  template: `
    <header>
      <app-avatar user="ana"></app-avatar>
      <app-badge label="pro"></app-badge>
    </header>
  `,
})
export class HeaderComponent {}
Try it live
📊 Production Insight
Map selectors to imports while writing, not after errors appear. Typing-time discipline beats debugging-time archaeology every week.
🎯 Key Takeaway
Imports arrays are per-template scopes, not shared pools; every selector used must be listed exactly where it's used.

NgModule vs Standalone Wiring Compared

NgModule wiring and standalone wiring solve the same scoping problem from opposite ends. A module declares its components once and imports shared dependencies once for all its templates. A standalone component imports its own dependencies once for its own template. Mixed codebases must respect both directions: modules consume standalone components through their imports array, while standalone components consume modules, or other standalone pieces, through theirs.

Two crossings are illegal and produce confusing errors. A non-standalone component can't appear in a standalone imports array; import its owning module instead. A standalone component can't appear in a module's declarations; list it under imports. The error messages name the violation precisely, but developers misread them as missing-dependency errors and add more wrong entries. Read the message literally: it states which crossing you attempted.

Migration strategy follows the dependency leaves inward. Convert leaf components with no children first: their imports arrays are small and verifiable. Then convert features whose children are already standalone. Convert bootstrap and routing last, when providers can move to functional equivalents. Verify each step with a production build, because dev-server tolerance hides wiring gaps that AOT exposes at the worst moment.

src/app/shared.module.tsTYPESCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './header.component';

@NgModule({
  declarations: [],
  // Standalone pieces enter modules through imports.
  imports: [CommonModule, HeaderComponent],
  exports: [HeaderComponent],
})
export class SharedModule {}

// Standalone consumer needs no module at all:
// imports: [HeaderComponent] in the using component.
Try it live
📊 Production Insight
Illegal crossings produce errors naming the violation, not a missing file. Read the message literally instead of adding more imports.
🎯 Key Takeaway
Modules share per feature while standalone declares per template; respect each dependency's declared style at every crossing.

Router and Structural Directives Need Imports Too

Router directives surprise migrators because routing feels like infrastructure that should just work. It doesn't: routerLink, routerLinkActive, and router-outlet are directives like any other, entering standalone templates only through RouterModule imports. A component that navigated fine under a routing module fails standalone with unknown-property errors on routerLink, while the route configuration itself remains perfectly valid.

Structural directives follow the same rule through CommonModule. A standalone template using ngIf or ngFor without importing CommonModule errors on the directive while custom selectors resolve fine, which sends developers hunting through component imports for a problem in module imports. Memorize the pairing: control flow needs CommonModule, two-way forms need FormsModule, routing needs RouterModule. Each is one import entry beside your components.

Scaffold discipline prevents the whole category. Team snippets for new standalone components should pre-import CommonModule and RouterModule where the feature needs them, leaving developers to add only business components. Review checklists should ask which directives the template uses, not just which components. Directives are invisible in the rendered page but mandatory in the imports array.

src/app/nav.component.tsTYPESCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AvatarComponent } from './avatar.component';

@Component({
  selector: 'app-nav',
  standalone: true,
  imports: [RouterModule, AvatarComponent],
  template: `
    <nav>
      <a routerLink="/team">Team</a>
      <app-avatar user="ana"></app-avatar>
    </nav>
    <router-outlet></router-outlet>
  `,
})
export class NavComponent {}
Try it live
📊 Production Insight
Custom selectors resolving while directives fail means a helper module is missing, not a component. Check CommonModule and RouterModule first.
🎯 Key Takeaway
Router and structural directives are opt-in imports per component; scaffold them by default and review templates for directive usage.

Providers Move Too: the importProvidersFrom Bridge

Providers follow a parallel migration path that intersects imports at bootstrap. NgModules contributed providers through their imports; standalone apps contribute them through the providers array in app.config.ts or bootstrapApplication. Deleting a module import during migration removes its provider recipes as well as its directives, which is how a component conversion produces a NullInjectorError two routes away from the edited file.

importProvidersFrom is the sanctioned bridge for half-migrated apps. It pulls an unmigrated module's providers into the standalone bootstrap context without converting the module itself. Use it for third-party modules lacking functional providers, and replace each bridge with a functional equivalent like provideHttpClient() as libraries modernize. Every bridge deserves a comment naming the module and the planned replacement, or bridges become permanent.

Audit deletions against additions during every migration PR. For each removed module import, answer two questions: which directives did it supply, and which providers did it register. Directives move to component imports arrays; providers move to app.config.ts. PRs that answer both explicitly merge safely; PRs that move syntax without tracing semantics break routes. The checklist is short but non-negotiable.

src/app/app.config.tsTYPESCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { importProvidersFrom } from '@angular/core';
import { LegacyModule } from './legacy.module';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
    // Bridge for modules that haven't migrated yet.
    importProvidersFrom(LegacyModule),
  ],
};
Try it live
📊 Production Insight
A NullInjectorError appearing after a component conversion means a deleted module took its providers along. Bridge or replace them in app.config.ts.
🎯 Key Takeaway
Module deletion removes providers as well as directives, so every removed import needs both a directives home and a providers home.

Third-Party Libraries Across Both Eras

Third-party libraries split across both eras, and each demands its own wiring. A standalone UI component imports directly into your imports array. A module-based UI kit imports as a module, granting its exported components to your template. Guessing wrong produces errors about unexpected imports or unknown elements that look like version bugs but are really style mismatches.

The decorator is the ground truth when docs lag. Open the library source or its type definitions and check for standalone: true on the component. Present means direct import; absent means import the owning module. Docs for popular kits usually state the style per version, but transitive dependencies and beta releases often trail behind. Thirty seconds reading the decorator beats thirty minutes fighting the compiler.

Pin and verify library wiring in CI. A production build over a lockfile catches renames, style changes, and dropped exports before they reach main. When upgrading UI kits, grep templates for affected selectors first and update them in the same commit as the version bump. Library imports are contracts with external authors; treat version bumps as renegotiations, not chores. Renegotiate carefully and the integration stays quiet for months.

📊 Production Insight
Library import errors that look like version bugs are usually style mismatches. Check the decorator before downgrading the package.
🎯 Key Takeaway
Wire each library piece the way its author declared it, using the standalone flag in its decorator as ground truth.

The Selector Roll Call That Prevents All of This

A systematic pass prevents wiring bugs from surviving to build time. Read the template top to bottom, list every custom selector, directive, and pipe, and map each to its owning file and standalone status. Open the imports array and check off each entry. Unchecked items are future compiler errors; check them now while the template's intent is fresh in mind.

Do this pass at creation time, not review time. The author holds the full context of which pieces the template needs, while reviewers reconstruct it from diffs. A two-minute roll call during writing saves a twenty-minute debugging session later plus the CI cycle in between. Make it part of the definition of done for every standalone component.

Enforce it mechanically where humans slip. Lint rules flag unknown selectors at PR time, real-template specs fail on missing imports, and production builds verify the whole graph. Human discipline starts the habit; automation keeps it alive across team growth and deadline pressure. Components maintained this way import exactly what they use, no more and no less. Roll calls take two minutes and save twenty; make them automatic and the savings compound across every sprint and every new hire.

💡The Selector Roll Call
List every selector in the template, map each to its owner, and verify the imports entry. That pass catches wiring gaps while the context is still fresh.
📊 Production Insight
Two minutes of roll call during writing beats twenty minutes of debugging plus a CI cycle. Make it definition-of-done for components.
🎯 Key Takeaway
Audit every template's selectors against its imports array at creation time, then enforce the habit with lint, specs, and builds.
● Production incidentPOST-MORTEMseverity: high

The Codemod That Emptied Every Header for 41 Minutes

Symptom
Every page header rendered blank starting 10:15 AM after the conversion deploy, with consoles full of unknown-element errors for app-avatar and routerLink. Page bodies loaded fine because feature components hadn't been converted yet. The first responder suspected a CSS regression for eighteen minutes before reading the console.
Assumption
The team assumed the migration tooling carried imports automatically and that green unit tests proved the templates. Every spec mocked child components, so none compiled the real imports arrays. The reviewer approved a mechanical-looking diff without mapping selectors to imports.
Root cause
A codemod converted the shared header components to standalone but didn't carry their imports arrays along: AvatarComponent, RouterModule directives, and CommonModule were left behind. Each header's template referenced selectors its new scope couldn't resolve, so the AOT build failed and the deploy pipeline shipped nothing workable. Unit tests mocked all children, so they passed against templates that could never compile.
Fix
The engineer added AvatarComponent and the other missing pieces to each header's imports array, re-ran the production build clean, and deployed. The follow-up required real-template specs for shared components, added ng build to CI, and published a migration checklist mapping every selector to its import.
Key lesson
  • Codemods move syntax, not semantics: every selector in every template needs a human-verified imports entry after conversion.
  • Mocked specs can't validate imports arrays, so shared components need specs compiling real templates with real imports.
  • Production AOT builds in CI catch wiring errors in the PR that introduced them, when the selector context is still fresh.
Production debug guideFive standalone wiring failures and the exact import each one needs.5 entries
Symptom · 01
'app-x' is not a known element after converting to standalone
→
Fix
Copy the unknown selector from the error and grep the codebase for its component definition. Open the using component's imports array and check for it. Add the standalone component there, or the owning NgModule if it isn't standalone. Restart ng serve to clear stale compilation and confirm the selector resolves.
Symptom · 02
Router directives error while navigation config is correct
→
Fix
Check whether the failing bindings are routerLink, routerOutlet, or routerLinkActive. Open the component's imports and look for RouterModule or the router directives. Add the missing import, save, and confirm navigation directives compile. Repeat for every routed component, since router imports never propagate.
Symptom · 03
A library component errors no matter how you import it
→
Fix
Open the third-party piece's source or docs and check for standalone: true. If standalone, import it directly. If not, import its NgModule instead. Adjust the imports array accordingly and rebuild. Never guess: the decorator is the ground truth when docs lag behind.
Symptom · 04
Structural directives fail while custom components resolve
→
Fix
Look at which syntax fails: ngIf and ngFor need CommonModule, [(ngModel)] needs FormsModule. Add the helper module to the standalone imports array alongside your components. Save and confirm the structural directives compile while custom selectors already resolve.
Symptom · 05
Half the routes break during a partial NgModule migration
→
Fix
List every selector in the broken templates and map each to its owner and standalone status. Convert leaf components first, then features, then bootstrap providers, verifying with ng build at each step. Add the imports checklist to the migration PR template so no route merges without its dependencies.
Standalone Import Failures Compared
Root CauseHow to ConfirmFixPrevention
Component missing from the using component's importsNG8001 names your selector; the file exists but no imports entry covers itAdd the component to the using component's imports arrayChecklist per component: every selector in the template maps to an import
Router directives never importedrouterLink or routerOutlet errors while navigation config is fineImport RouterModule or the router directives in the componentInclude router imports in every routed component scaffold
Non-standalone component imported directlyError about unexpected imports; the dependency lacks standalone: trueImport its owning NgModule instead, or migrate it to standaloneAudit third-party docs for standalone status before importing
CommonModule missing for ngIf and ngForStructural directives error while custom selectors resolve fineAdd CommonModule to the standalone imports arraySnippet templates with CommonModule pre-imported
⚙ Quick Reference
4 commands from this guide
FileCommand / CodePurpose
srcappheader.component.ts@Component({The Standalone Imports Mental Model
srcappshared.module.ts@NgModule({NgModule vs Standalone Wiring Compared
srcappnav.component.ts@Component({Router and Structural Directives Need Imports Too
srcappapp.config.tsexport const appConfig: ApplicationConfig = {Providers Move Too

Key takeaways

1
Standalone components resolve every template dependency through their own imports array; nothing is inherited.
2
NgModule apps share dependencies per module, while standalone apps declare them per component template.
3
Router directives and CommonModule structural directives are opt-in imports like everything else.
4
Modules can import standalone components, but standalone imports arrays accept only standalone pieces or modules.
5
importProvidersFrom bridges unmigrated modules into bootstrap providers; prefer functional providers where they exist.
6
Migrate leaf-first with real-template specs and AOT builds so each PR carries its imports along.

Common mistakes to avoid

5 patterns
×

Putting a standalone component in providers instead of imports

Symptom
The unknown-element error persists alongside an invalid-provider error, because a component class is not a valid provider recipe.
Fix
List the dependency in imports, never in providers. Components, directives, and pipes are importable; only services and values are providable.
×

Forgetting RouterModule for routerLink and routerOutlet

Symptom
Router directives error as unknown properties or elements even though routing works elsewhere, because the using component never imported them.
Fix
Add RouterModule or the specific router directives to the component's imports. Routing directives are opt-in per component like everything else.
×

Importing an NgModule-based component directly

Symptom
A cryptic error about unexpected directive imports, because non-standalone components can't appear in an imports array.
Fix
Open the dependency's source and check its decorator: standalone true means imports, otherwise declarations plus exports. Wire each dependency the way its author declared it.
×

Declaring a standalone component in an NgModule

Symptom
A compiler error stating the component is already standalone, caused by listing it under declarations instead of imports.
Fix
Move it to the module's imports array. Standalone components are imported everywhere, including into NgModules.
×

Using ngIf or ngFor without importing CommonModule

Symptom
Structural directives error as unknown properties in a standalone template that otherwise compiles, because CommonModule was never imported.
Fix
Add CommonModule to the standalone component's imports. Structural directives travel with it, not with the framework default.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
Why must standalone components list their own imports?
Q02SENIOR
Contrast NgModule wiring with standalone wiring.
Q03SENIOR
What is importProvidersFrom for?
Q04SENIOR
How do you mix standalone and NgModule dependencies?
Q05SENIOR
How do you debug a migration that broke half the routes?
Q01 of 05JUNIOR

Why must standalone components list their own imports?

ANSWER
Because standalone components have no NgModule carrying shared dependencies. Each component declares its template dependencies in its own imports array: other components, directives, pipes, and modules like CommonModule or FormsModule. Without the entry, the compiler can't resolve the selector and reports NG8001.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Do child components inherit their parent's imports?
02
Can an NgModule use a standalone component?
03
What if the import exists but the error persists?
04
Should I migrate all modules at once?
05
Do I repeat shared imports in every component?
06
How do I know if a library piece is standalone?
N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Everything here is grounded in real deployments.

Follow
✓ Verified
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
🔥

That's Angular. Mark it forged?

5 min read · try the examples if you haven't

←
Previous
Angular Http Failure Response for Unknown URL 0
6 / 6 · Angular