Lesson 3 of 30
A standalone component declares its own dependencies right there in the decorator — no NgModule required. This has been the default since Angular v17, and it changes how you read every component.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
For most of Angular's history, every component had to belong to an NgModule — a separate file whose job was declaring which components existed, which other modules' features they could use (CommonModule for *ngIf, FormsModule for ngModel, and so on), and which components were exported for use elsewhere. To use a directive, you didn't import it where you used it — you imported the module that declared it, somewhere else entirely, and hoped someone had remembered to add it.
Standalone components remove that indirection. A component lists exactly what it needs — other components, directives, pipes — in an imports array on its own @Component decorator. No module file, no separate declarations list, no hunting through the app to figure out why a directive isn't working. This has been the default for new components since Angular v17, and as of today it's simply how you build an Angular app; NgModules still exist and you'll run into them in older codebases, but they're legacy structure now, not the thing to reach for.
// product-card.component.ts
import { Component, input } from '@angular/core';
import { RouterLink } from '@angular/router';
import { NgOptimizedImage } from '@angular/common';
import { PriceTagComponent } from '../price-tag/price-tag.component';
@Component({
selector: 'app-product-card',
standalone: true,
imports: [RouterLink, NgOptimizedImage, PriceTagComponent],
template: `
<a [routerLink]="['/products', product().id]">
<img [ngSrc]="product().imageUrl" width="240" height="240" alt={{ product().name }} />
<h3>{{ product().name }}</h3>
<app-price-tag [amount]="product().price" />
</a>
`,
})
export class ProductCardComponent {
product = input.required<{ id: string; name: string; imageUrl: string; price: number }>();
}
Read the imports array and you know exactly what this template can use: routerLink because RouterLink is imported, ngSrc because NgOptimizedImage is imported, <app-price-tag> because PriceTagComponent is imported directly — a component importing another component, the way you'd import anything else. There's no module file anywhere that secretly makes RouterLink available; if you deleted it from this array, the template would fail to compile with a clear "unknown property" error pointing right at this file.
And bootstrapping reflects the same shift — from the CLI lesson, bootstrapApplication(AppComponent, appConfig) takes a standalone component directly, no root AppModule wrapping it.
imports array — rather than globally, in a shared NgModule. A standalone component can still import a dozen things; it just says so, right there.RouterLink in one component's imports array only breaks that one component's template — worse locally-visible errors, but a much smaller blast radius to debug.React developers already do this without thinking about it: import Button from './Button' at the top of the file that uses it, nowhere else. Standalone components are Angular arriving at the same idea — a component's dependency list lives with the component, not in some separate registration file you have to go find. The component-anatomy lesson picks up right where this one leaves off, looking at everything else the @Component decorator controls.